build: Move sources into src/ again
authorColin Walters <walters@verbum.org>
Mon, 14 Nov 2011 20:39:38 +0000 (15:39 -0500)
committerColin Walters <walters@verbum.org>
Mon, 14 Nov 2011 20:39:38 +0000 (15:39 -0500)
This is necessary if we want to build when srcdir == builddir,
otherwise we blow up because "ostree" is a source directory and a
binary.

79 files changed:
Makefile-libostree.am
Makefile-osbuild.am
Makefile-ostree.am
Makefile-otutil.am
libotutil/ot-gio-utils.c [deleted file]
libotutil/ot-gio-utils.h [deleted file]
libotutil/ot-glib-compat.c [deleted file]
libotutil/ot-glib-compat.h [deleted file]
libotutil/ot-opt-utils.c [deleted file]
libotutil/ot-opt-utils.h [deleted file]
libotutil/ot-unix-utils.c [deleted file]
libotutil/ot-unix-utils.h [deleted file]
libotutil/ot-variant-utils.c [deleted file]
libotutil/ot-variant-utils.h [deleted file]
libotutil/otutil.h [deleted file]
osbuild/main.c [deleted file]
osbuild/ob-builtin-buildone.c [deleted file]
osbuild/ob-builtins.h [deleted file]
osbuild/osbuild-raw-makeinstall.c [deleted file]
osbuild/ostree-buildone [deleted file]
osbuild/ostree-buildone-make [deleted file]
osbuild/ostree-buildone-makeinstall-split-artifacts [deleted file]
ostree/main.c [deleted file]
ostree/ot-builtin-checkout.c [deleted file]
ostree/ot-builtin-commit.c [deleted file]
ostree/ot-builtin-compose.c [deleted file]
ostree/ot-builtin-diff.c [deleted file]
ostree/ot-builtin-fsck.c [deleted file]
ostree/ot-builtin-init.c [deleted file]
ostree/ot-builtin-log.c [deleted file]
ostree/ot-builtin-pull.c [deleted file]
ostree/ot-builtin-remote.c [deleted file]
ostree/ot-builtin-rev-parse.c [deleted file]
ostree/ot-builtin-run-triggers.c [deleted file]
ostree/ot-builtin-show.c [deleted file]
ostree/ot-builtins.h [deleted file]
src/libostree/ostree-checkout.c [new file with mode: 0644]
src/libostree/ostree-checkout.h [new file with mode: 0644]
src/libostree/ostree-core.c [new file with mode: 0644]
src/libostree/ostree-core.h [new file with mode: 0644]
src/libostree/ostree-repo-file-enumerator.c [new file with mode: 0644]
src/libostree/ostree-repo-file-enumerator.h [new file with mode: 0644]
src/libostree/ostree-repo-file.c [new file with mode: 0644]
src/libostree/ostree-repo-file.h [new file with mode: 0644]
src/libostree/ostree-repo.c [new file with mode: 0644]
src/libostree/ostree-repo.h [new file with mode: 0644]
src/libostree/ostree.h [new file with mode: 0644]
src/libotutil/ot-gio-utils.c [new file with mode: 0644]
src/libotutil/ot-gio-utils.h [new file with mode: 0644]
src/libotutil/ot-glib-compat.c [new file with mode: 0644]
src/libotutil/ot-glib-compat.h [new file with mode: 0644]
src/libotutil/ot-opt-utils.c [new file with mode: 0644]
src/libotutil/ot-opt-utils.h [new file with mode: 0644]
src/libotutil/ot-unix-utils.c [new file with mode: 0644]
src/libotutil/ot-unix-utils.h [new file with mode: 0644]
src/libotutil/ot-variant-utils.c [new file with mode: 0644]
src/libotutil/ot-variant-utils.h [new file with mode: 0644]
src/libotutil/otutil.h [new file with mode: 0644]
src/osbuild/main.c [new file with mode: 0644]
src/osbuild/ob-builtin-buildone.c [new file with mode: 0644]
src/osbuild/ob-builtins.h [new file with mode: 0644]
src/osbuild/osbuild-raw-makeinstall.c [new file with mode: 0644]
src/osbuild/ostree-buildone [new file with mode: 0644]
src/osbuild/ostree-buildone-make [new file with mode: 0644]
src/osbuild/ostree-buildone-makeinstall-split-artifacts [new file with mode: 0644]
src/ostree/main.c [new file with mode: 0644]
src/ostree/ot-builtin-checkout.c [new file with mode: 0644]
src/ostree/ot-builtin-commit.c [new file with mode: 0644]
src/ostree/ot-builtin-compose.c [new file with mode: 0644]
src/ostree/ot-builtin-diff.c [new file with mode: 0644]
src/ostree/ot-builtin-fsck.c [new file with mode: 0644]
src/ostree/ot-builtin-init.c [new file with mode: 0644]
src/ostree/ot-builtin-log.c [new file with mode: 0644]
src/ostree/ot-builtin-pull.c [new file with mode: 0644]
src/ostree/ot-builtin-remote.c [new file with mode: 0644]
src/ostree/ot-builtin-rev-parse.c [new file with mode: 0644]
src/ostree/ot-builtin-run-triggers.c [new file with mode: 0644]
src/ostree/ot-builtin-show.c [new file with mode: 0644]
src/ostree/ot-builtins.h [new file with mode: 0644]

index 84737a78d997a39ee3bade79126ae6bbbeb3ee0d..091d152aeffbe91a864790f3cafd301ffc68b68e 100644 (file)
 
 noinst_LTLIBRARIES += libostree.la
 
-libostree_la_SOURCES = libostree/ostree.h \
-       libostree/ostree-core.c \
-       libostree/ostree-core.h \
-       libostree/ostree-repo.c \
-       libostree/ostree-repo.h \
-       libostree/ostree-repo-file.c \
-       libostree/ostree-repo-file.h \
-       libostree/ostree-repo-file-enumerator.c \
-       libostree/ostree-repo-file-enumerator.h \
-       libostree/ostree-checkout.c \
-       libostree/ostree-checkout.h \
+libostree_la_SOURCES = src/libostree/ostree.h \
+       src/libostree/ostree-core.c \
+       src/libostree/ostree-core.h \
+       src/libostree/ostree-repo.c \
+       src/libostree/ostree-repo.h \
+       src/libostree/ostree-repo-file.c \
+       src/libostree/ostree-repo-file.h \
+       src/libostree/ostree-repo-file-enumerator.c \
+       src/libostree/ostree-repo-file-enumerator.h \
+       src/libostree/ostree-checkout.c \
+       src/libostree/ostree-checkout.h \
        $(NULL)
-libostree_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -I$(srcdir)/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+libostree_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
 libostree_la_LIBADD = libotutil.la $(OT_COREBIN_DEP_LIBS)
index 3f2e56b591171e663d22f0e36bc7bbea2ace7830..8c2b1bd3158510e4790faf24c25c956a9768a0e0 100644 (file)
@@ -15,7 +15,7 @@
 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 # Boston, MA 02111-1307, USA.
 
-bin_SCRIPTS += osbuild/ostree-buildone \
-       osbuild/ostree-buildone-make \
-       osbuild/ostree-buildone-makeinstall-split-artifacts \
+bin_SCRIPTS += src/osbuild/ostree-buildone \
+       src/osbuild/ostree-buildone-make \
+       src/osbuild/ostree-buildone-makeinstall-split-artifacts \
        $(NULL)
index b39f9bd86307fd687f722963e4b954a0c623140d..0bb7cdec6028b5d4257d40a80053d2f254aca929 100644 (file)
 
 bin_PROGRAMS += ostree
 
-ostree_SOURCES = ostree/main.c \
-       ostree/ot-builtins.h \
-       ostree/ot-builtin-checkout.c \
-       ostree/ot-builtin-commit.c \
-       ostree/ot-builtin-compose.c \
-       ostree/ot-builtin-diff.c \
-       ostree/ot-builtin-fsck.c \
-       ostree/ot-builtin-init.c \
-       ostree/ot-builtin-log.c \
-       ostree/ot-builtin-run-triggers.c \
-       ostree/ot-builtin-remote.c \
-       ostree/ot-builtin-rev-parse.c \
-       ostree/ot-builtin-show.c \
+ostree_SOURCES = src/ostree/main.c \
+       src/ostree/ot-builtins.h \
+       src/ostree/ot-builtin-checkout.c \
+       src/ostree/ot-builtin-commit.c \
+       src/ostree/ot-builtin-compose.c \
+       src/ostree/ot-builtin-diff.c \
+       src/ostree/ot-builtin-fsck.c \
+       src/ostree/ot-builtin-init.c \
+       src/ostree/ot-builtin-log.c \
+       src/ostree/ot-builtin-run-triggers.c \
+       src/ostree/ot-builtin-remote.c \
+       src/ostree/ot-builtin-rev-parse.c \
+       src/ostree/ot-builtin-show.c \
        $(NULL)
 
 if USE_LIBSOUP_GNOME
-ostree_SOURCES += ostree/ot-builtin-pull.c
+ostree_SOURCES += src/ostree/ot-builtin-pull.c
 endif
-ostree_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -I$(srcdir)/libostree -I$(srcdir)/ostree  -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
+ostree_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(srcdir)/src/ostree  -DLOCALEDIR=\"$(datadir)/locale\" $(OT_COREBIN_DEP_CFLAGS)
 ostree_LDADD = libotutil.la libostree.la $(OT_COREBIN_DEP_LIBS)
index ff729bdc6d12a1b85a8d75c1d42ce773e1004ed3..6d4e492cf60516b59aeb51b2a798a02b0f5dae8e 100644 (file)
 noinst_LTLIBRARIES += libotutil.la
 
 libotutil_la_SOURCES = \
-       libotutil/ot-opt-utils.c \
-       libotutil/ot-opt-utils.h \
-       libotutil/ot-unix-utils.c \
-       libotutil/ot-unix-utils.h \
-       libotutil/ot-variant-utils.c \
-       libotutil/ot-variant-utils.h \
-       libotutil/ot-gio-utils.c \
-       libotutil/ot-gio-utils.h \
-       libotutil/ot-glib-compat.c \
-       libotutil/ot-glib-compat.h \
-       libotutil/otutil.h \
+       src/libotutil/ot-opt-utils.c \
+       src/libotutil/ot-opt-utils.h \
+       src/libotutil/ot-unix-utils.c \
+       src/libotutil/ot-unix-utils.h \
+       src/libotutil/ot-variant-utils.c \
+       src/libotutil/ot-variant-utils.h \
+       src/libotutil/ot-gio-utils.c \
+       src/libotutil/ot-gio-utils.h \
+       src/libotutil/ot-glib-compat.c \
+       src/libotutil/ot-glib-compat.h \
+       src/libotutil/otutil.h \
        $(NULL)
-libotutil_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
+libotutil_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -DLOCALEDIR=\"$(datadir)/locale\" $(GIO_UNIX_CFLAGS)
 libotutil_la_LIBADD = $(GIO_UNIX_LIBS)
diff --git a/libotutil/ot-gio-utils.c b/libotutil/ot-gio-utils.c
deleted file mode 100644 (file)
index bae8037..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-#include <gio/gunixinputstream.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-gboolean
-ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
-{
-  GFile *dir;
-  GError *temp_error = NULL;
-  gboolean ret = FALSE;
-
-  dir = g_file_new_for_path (path);
-  if (with_parents)
-    ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
-  else
-    ret = g_file_make_directory (dir, NULL, &temp_error);
-  if (!ret)
-    {
-      if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-      else
-        g_clear_error (&temp_error);
-    }
-
-  ret = TRUE;
- out:
-  g_clear_object (&dir);
-  return ret;
-}
-
-
-char *
-ot_util_get_file_contents_utf8 (const char *path,
-                                GError    **error)
-{
-  GFile *file = NULL;
-  char *ret = NULL;
-
-  file = ot_util_new_file_for_path (path);
-  if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
-    goto out;
-
- out:
-  g_clear_object (&file);
-  return ret;
-}
-
-gboolean
-ot_util_gfile_load_contents_utf8 (GFile         *file,
-                                  GCancellable  *cancellable,
-                                  char         **contents_out,
-                                  char         **etag_out,
-                                  GError       **error)
-{
-  char *ret_contents = NULL;
-  char *ret_etag = NULL;
-  gsize len;
-  gboolean ret = FALSE;
-
-  if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
-    goto out;
-  if (!g_utf8_validate (ret_contents, len, NULL))
-    {
-      g_set_error (error,
-                   G_IO_ERROR,
-                   G_IO_ERROR_INVALID_DATA,
-                   "Invalid UTF-8");
-      goto out;
-    }
-
-  if (contents_out)
-    *contents_out = ret_contents;
-  else
-    g_free (ret_contents);
-  ret_contents = NULL;
-  if (etag_out)
-    *etag_out = ret_etag;
-  else
-    g_free (ret_etag);
-  ret_etag = NULL;
-  ret = TRUE;
- out:
-  g_free (ret_contents);
-  g_free (ret_etag);
-  return ret;
-}
-
-GInputStream *
-ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
-{
-  GInputStream *ret = NULL;
-  int fd;
-  int flags = O_RDONLY;
-  char *path = NULL;
-
-  path = g_file_get_path (file);
-#ifdef O_NOATIME
-  flags |= O_NOATIME;
-#endif
-  fd = open (path, flags);
-  if (fd < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
-  
- out:
-  g_free (path);
-  return ret;
-}
-
-/* Like g_file_new_for_path, but only do local stuff, not GVFS */
-GFile *
-ot_util_new_file_for_path (const char *path)
-{
-  return g_vfs_get_file_for_path (g_vfs_get_local (), path);
-}
-
-const char *
-ot_gfile_get_path_cached (GFile *file)
-{
-  const char *path;
-
-  path = g_object_get_data ((GObject*)file, "ostree-file-path");
-  if (!path)
-    {
-      path = g_file_get_path (file);
-      g_object_set_data_full ((GObject*)file, "ostree-file-path", (char*)path, (GDestroyNotify)g_free);
-    }
-  return path;
-}
-
-
-const char *
-ot_gfile_get_basename_cached (GFile *file)
-{
-  const char *name;
-
-  name = g_object_get_data ((GObject*)file, "ostree-file-name");
-  if (!name)
-    {
-      name = g_file_get_basename (file);
-      g_object_set_data_full ((GObject*)file, "ostree-file-name", (char*)name, (GDestroyNotify)g_free);
-    }
-  return name;
-}
diff --git a/libotutil/ot-gio-utils.h b/libotutil/ot-gio-utils.h
deleted file mode 100644 (file)
index 8435b87..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GIO_UTILS_H__
-#define __OSTREE_GIO_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-GFile *ot_util_new_file_for_path (const char *path);
-
-const char *ot_gfile_get_path_cached (GFile *file);
-
-const char *ot_gfile_get_basename_cached (GFile *file);
-
-gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
-
-char * ot_util_get_file_contents_utf8 (const char *path, GError    **error);
-
-gboolean ot_util_gfile_load_contents_utf8 (GFile         *file,
-                                           GCancellable  *cancellable,
-                                           char         **contents_out,
-                                           char         **etag_out,
-                                           GError       **error);
-
-GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
-
-G_END_DECLS
-
-#endif
diff --git a/libotutil/ot-glib-compat.c b/libotutil/ot-glib-compat.c
deleted file mode 100644 (file)
index 44181b9..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-#if GLIB_CHECK_VERSION(2,32,0)
-/* nothing */
-#else
-/* Code copied from glib/glib/genviron.c */
-/* GLIB - Library of useful routines for C programming
- * Copyright (C) 1995-1998  Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
- * file for a list of people on the GLib Team.  See the ChangeLog
- * files for a list of changes.  These files are distributed with
- * GLib at ftp://ftp.gtk.org/pub/gtk/. 
- */
-
-static gint
-ot_g_environ_find (gchar       **envp,
-                const gchar  *variable)
-{
-  gint len, i;
-
-  len = strlen (variable);
-
-  for (i = 0; envp[i]; i++)
-    {
-      if (strncmp (envp[i], variable, len) == 0 &&
-          envp[i][len] == '=')
-        return i;
-    }
-
-  return -1;
-}
-
-const gchar *
-ot_g_environ_getenv (gchar       **envp,
-                  const gchar  *variable)
-{
-  gint index;
-
-  g_return_val_if_fail (envp != NULL, NULL);
-  g_return_val_if_fail (variable != NULL, NULL);
-
-  index = ot_g_environ_find (envp, variable);
-  if (index != -1)
-    return envp[index] + strlen (variable) + 1;
-  else
-    return NULL;
-}
-
-gchar **
-ot_g_environ_setenv (gchar       **envp,
-                  const gchar  *variable,
-                  const gchar  *value,
-                  gboolean      overwrite)
-{
-  gint index;
-
-  g_return_val_if_fail (envp != NULL, NULL);
-  g_return_val_if_fail (variable != NULL, NULL);
-  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
-
-  index = ot_g_environ_find (envp, variable);
-  if (index != -1)
-    {
-      if (overwrite)
-        {
-          g_free (envp[index]);
-          envp[index] = g_strdup_printf ("%s=%s", variable, value);
-        }
-    }
-  else
-    {
-      gint length;
-
-      length = g_strv_length (envp);
-      envp = g_renew (gchar *, envp, length + 2);
-      envp[length] = g_strdup_printf ("%s=%s", variable, value);
-      envp[length + 1] = NULL;
-    }
-
-  return envp;
-}
-
-gchar **
-ot_g_environ_unsetenv (gchar       **envp,
-                    const gchar  *variable)
-{
-  gint len;
-  gchar **e, **f;
-
-  g_return_val_if_fail (envp != NULL, NULL);
-  g_return_val_if_fail (variable != NULL, NULL);
-  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
-
-  len = strlen (variable);
-
-  /* Note that we remove *all* environment entries for
-   * the variable name, not just the first.
-   */
-  e = f = envp;
-  while (*e != NULL)
-    {
-      if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
-        {
-          *f = *e;
-          f++;
-        }
-      e++;
-    }
-  *f = NULL;
-
-  return envp;
-}
-
-#endif
diff --git a/libotutil/ot-glib-compat.h b/libotutil/ot-glib-compat.h
deleted file mode 100644 (file)
index 18d7d3d..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_GLIB_COMPAT_H__
-#define __OSTREE_GLIB_COMPAT_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-#if GLIB_CHECK_VERSION(2,32,0)
-#define ot_g_environ_getenv g_environ_getenv
-#define ot_g_environ_setenv g_environ_setenv
-#define ot_g_environ_unsetenv g_environ_unsetenv
-#else
-const gchar *
-ot_g_environ_getenv (gchar       **envp,
-                     const gchar  *variable);
-
-gchar **
-ot_g_environ_setenv (gchar       **envp,
-                     const gchar  *variable,
-                     const gchar  *value,
-                     gboolean      overwrite);
-
-gchar **
-ot_g_environ_unsetenv (gchar       **envp,
-                       const gchar  *variable);
-#endif
-
-
-G_END_DECLS
-
-#endif
diff --git a/libotutil/ot-opt-utils.c b/libotutil/ot-opt-utils.c
deleted file mode 100644 (file)
index 750e5b6..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-void
-ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
-{
-  gchar *help = g_option_context_get_help (context, TRUE, NULL);
-  g_printerr ("%s\n", help);
-  g_free (help);
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
-}
diff --git a/libotutil/ot-opt-utils.h b/libotutil/ot-opt-utils.h
deleted file mode 100644 (file)
index fdcfd33..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_OPT_UTILS_H__
-#define __OSTREE_OPT_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
-
-G_END_DECLS
-
-#endif
diff --git a/libotutil/ot-unix-utils.c b/libotutil/ot-unix-utils.c
deleted file mode 100644 (file)
index 24fcf8b..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-unix-utils.h"
-
-#include <gio/gio.h>
-#include <gio/gunixoutputstream.h>
-
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <dirent.h>
-
-gboolean
-ot_util_spawn_pager (GOutputStream  **out_stream,
-                     GError         **error)
-{
-  const char *pager;
-  char *argv[2];
-  int stdin_fd;
-  pid_t pid;
-  gboolean ret = FALSE;
-  GOutputStream *ret_stream = NULL;
-
-  if (!isatty (1))
-    {
-      ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
-    }
-  else
-    {
-      pager = g_getenv ("GIT_PAGER");
-      if (pager == NULL)
-        pager = "less";
-      
-      argv[0] = (char*)pager;
-      argv[1] = NULL;
-      
-      if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
-                                     NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
-        {
-          g_prefix_error (error, "%s", "Failed to spawn pager: ");
-          goto out;
-        }
-      
-      ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
-    }
-
-  *out_stream = ret_stream;
-  ret_stream = NULL;
-  ret = TRUE;
- out:
-  g_clear_object (&ret_stream);
-  return ret;
-}
-
-static int
-compare_filenames_by_component_length (const char *a,
-                                       const char *b)
-{
-  char *a_slash, *b_slash;
-
-  a_slash = strchr (a, '/');
-  b_slash = strchr (b, '/');
-  while (a_slash && b_slash)
-    {
-      a = a_slash + 1;
-      b = b_slash + 1;
-      a_slash = strchr (a, '/');
-      b_slash = strchr (b, '/');
-    }
-  if (a_slash)
-    return -1;
-  else if (b_slash)
-    return 1;
-  else
-    return 0;
-}
-
-GPtrArray *
-ot_util_sort_filenames_by_component_length (GPtrArray *files)
-{
-  GPtrArray *array = g_ptr_array_sized_new (files->len);
-  memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
-  g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
-  return array;
-}
-
-int
-ot_util_count_filename_components (const char *path)
-{
-  int i = 0;
-
-  while (path)
-    {
-      i++;
-      path = strchr (path, '/');
-      if (path)
-        path++;
-    }
-  return i;
-}
-
-gboolean
-ot_util_filename_has_dotdot (const char *path)
-{
-  char *p;
-  char last;
-
-  if (strcmp (path, "..") == 0)
-    return TRUE;
-  if (g_str_has_prefix (path, "../"))
-    return TRUE;
-  p = strstr (path, "/..");
-  if (!p)
-    return FALSE;
-  last = *(p + 1);
-  return last == '\0' || last == '/';
-}
-
-GPtrArray *
-ot_util_path_split (const char *path)
-{
-  GPtrArray *ret = NULL;
-  const char *p;
-  const char *slash;
-  int i;
-
-  g_return_val_if_fail (path[0] != '/', NULL);
-
-  ret = g_ptr_array_new ();
-  g_ptr_array_set_free_func (ret, g_free);
-
-  p = path;
-  do {
-    slash = strchr (p, '/');
-    if (!slash)
-      {
-        g_ptr_array_add (ret, g_strdup (p));
-        p = NULL;
-      }
-    else
-      {
-        g_ptr_array_add (ret, g_strndup (p, slash - p));
-        p = slash + 1;
-      }
-  } while (p && *p);
-
-  /* Canonicalize by removing duplicate '.' */
-  for (i = ret->len-1; i >= 0; i--)
-    {
-      if (strcmp (ret->pdata[i], ".") == 0)
-        g_ptr_array_remove_index (ret, i);
-    }
-
-  return ret;
-}
-
-char *
-ot_util_path_join_n (const char *base, GPtrArray *components, int n)
-{
-  int max = MIN(n+1, components->len);
-  GPtrArray *subcomponents;
-  char *path;
-  int i;
-
-  subcomponents = g_ptr_array_new ();
-
-  if (base != NULL)
-    g_ptr_array_add (subcomponents, (char*)base);
-
-  for (i = 0; i < max; i++)
-    {
-      g_ptr_array_add (subcomponents, components->pdata[i]);
-    }
-  g_ptr_array_add (subcomponents, NULL);
-  
-  path = g_build_filenamev ((char**)subcomponents->pdata);
-  g_ptr_array_free (subcomponents, TRUE);
-  
-  return path;
-}
-
-void
-ot_util_set_error_from_errno (GError **error,
-                              gint     saved_errno)
-{
-  g_set_error_literal (error,
-                       G_IO_ERROR,
-                       0,
-                       g_strerror (saved_errno));
-  errno = saved_errno;
-}
-
-int
-ot_util_open_file_read (const char *path, GError **error)
-{
-  char *dirname = NULL;
-  char *basename = NULL;
-  DIR *dir = NULL;
-  int fd = -1;
-
-  dirname = g_path_get_dirname (path);
-  basename = g_path_get_basename (path);
-  dir = opendir (dirname);
-  if (dir == NULL)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-
-  fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
-
- out:
-  g_free (basename);
-  g_free (dirname);
-  if (dir != NULL)
-    closedir (dir);
-  return fd;
-}
-
-int
-ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
-{
-  int fd;
-  int flags = O_RDONLY;
-  
-#ifdef O_CLOEXEC
-  flags |= O_CLOEXEC;
-#endif
-#ifdef O_NOATIME
-  flags |= O_NOATIME;
-#endif
-  fd = openat (dirfd, name, flags);
-  if (fd < 0)
-    ot_util_set_error_from_errno (error, errno);
-  return fd;
-}
-
-void
-ot_util_fatal_literal (const char *msg)
-{
-  g_printerr ("%s\n", msg);
-  exit (1);
-}
-
-void
-ot_util_fatal_gerror (GError *error)
-{
-  g_assert (error != NULL);
-  ot_util_fatal_literal (error->message);
-}
diff --git a/libotutil/ot-unix-utils.h b/libotutil/ot-unix-utils.h
deleted file mode 100644 (file)
index 8b392cb..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UNIX_UTILS_H__
-#define __OSTREE_UNIX_UTILS_H__
-
-#include <gio/gio.h>
-
-/* I just put all this shit here. Sue me. */
-#include <sys/types.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdio.h>
-
-G_BEGIN_DECLS
-
-gboolean ot_util_spawn_pager (GOutputStream  **out_stream, GError         **error);
-
-void ot_util_fatal_literal (const char *msg) G_GNUC_NORETURN;
-
-void ot_util_fatal_gerror (GError *error) G_GNUC_NORETURN;
-
-gboolean ot_util_filename_has_dotdot (const char *path);
-
-GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
-
-GPtrArray* ot_util_path_split (const char *path);
-
-char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
-
-int ot_util_count_filename_components (const char *path);
-
-int ot_util_open_file_read (const char *path, GError **error);
-
-int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
-
-void ot_util_set_error_from_errno (GError **error, gint saved_errno);
-
-G_END_DECLS
-
-#endif
diff --git a/libotutil/ot-variant-utils.c b/libotutil/ot-variant-utils.c
deleted file mode 100644 (file)
index d450ed6..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "otutil.h"
-
-GHashTable *
-ot_util_variant_asv_to_hash_table (GVariant *variant)
-{
-  GHashTable *ret;
-  GVariantIter *viter;
-  char *key;
-  GVariant *value;
-  
-  ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
-  viter = g_variant_iter_new (variant);
-  while (g_variant_iter_next (viter, "{s@v}", &key, &value))
-    g_hash_table_replace (ret, key, g_variant_ref_sink (value));
-  
-  g_variant_iter_free (viter);
-  
-  return ret;
-}
-
-gboolean
-ot_util_variant_save (GFile *dest,
-                      GVariant *variant,
-                      GCancellable *cancellable,
-                      GError  **error)
-{
-  gboolean ret = FALSE;
-  GOutputStream *out = NULL;
-  gsize bytes_written;
-  
-  out = (GOutputStream*)g_file_replace (dest, NULL, 0, FALSE, cancellable, error);
-  if (!out)
-    goto out;
-
-  if (!g_output_stream_write_all (out,
-                                  g_variant_get_data (variant),
-                                  g_variant_get_size (variant),
-                                  &bytes_written,
-                                  cancellable,
-                                  error))
-    goto out;
-  if (!g_output_stream_close (out, cancellable, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  g_clear_object (&out);
-  return ret;
-}
-
-GVariant *
-ot_util_variant_take_ref (GVariant *variant)
-{
-#if GLIB_CHECK_VERSION(2,32,0)
-  return g_variant_take_ref (variant);
-#else
-  if (g_variant_is_floating (variant))
-    return g_variant_ref_sink (variant);
-  return variant;
-#endif
-}
-
-gboolean
-ot_util_variant_map (GFile *src,
-                     const GVariantType *type,
-                     GVariant **out_variant,
-                     GError  **error)
-{
-  gboolean ret = FALSE;
-  GMappedFile *mfile = NULL;
-  char *path = NULL;
-  GVariant *ret_variant = NULL;
-
-  path = g_file_get_path (src);
-  mfile = g_mapped_file_new (path, FALSE, error);
-  if (!mfile)
-    goto out;
-
-  ret_variant = g_variant_new_from_data (type,
-                                         g_mapped_file_get_contents (mfile),
-                                         g_mapped_file_get_length (mfile),
-                                         FALSE,
-                                         (GDestroyNotify) g_mapped_file_unref,
-                                         mfile);
-  mfile = NULL;
-  g_variant_ref_sink (ret_variant);
-  
-  ret = TRUE;
-  *out_variant = ret_variant;
-  ret_variant = NULL;
- out:
-  if (ret_variant)
-    g_variant_unref (ret_variant);
-  if (mfile)
-    g_mapped_file_unref (mfile);
-  g_free (path);
-  return ret;
-}
diff --git a/libotutil/ot-variant-utils.h b/libotutil/ot-variant-utils.h
deleted file mode 100644 (file)
index 57ce663..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_VARIANT_UTILS_H__
-#define __OSTREE_VARIANT_UTILS_H__
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-GHashTable *ot_util_variant_asv_to_hash_table (GVariant *variant);
-
-GVariant * ot_util_variant_take_ref (GVariant *variant);
-
-gboolean ot_util_variant_save (GFile *dest,
-                               GVariant *variant,
-                               GCancellable *cancellable,
-                               GError  **error);
-
-gboolean ot_util_variant_map (GFile *src,
-                              const GVariantType *type,
-                              GVariant **out_variant,
-                              GError  **error);
-
-G_END_DECLS
-
-#endif
diff --git a/libotutil/otutil.h b/libotutil/otutil.h
deleted file mode 100644 (file)
index a93dc84..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_UTIL_H__
-
-#include <ot-gio-utils.h>
-#include <ot-glib-compat.h>
-#include <ot-opt-utils.h>
-#include <ot-unix-utils.h>
-#include <ot-variant-utils.h>
-
-#endif
diff --git a/osbuild/main.c b/osbuild/main.c
deleted file mode 100644 (file)
index d481e99..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "ob-builtins.h"
-
-static OsbuildBuiltin builtins[] = {
-  { "buildone", osbuild_builtin_buildone, 0 },
-  { NULL }
-};
-
-static int
-usage (char **argv, gboolean is_error)
-{
-  OsbuildBuiltin *builtin = builtins;
-  void (*print_func) (const gchar *format, ...);
-
-  if (is_error)
-    print_func = g_printerr;
-  else
-    print_func = g_print;
-
-  print_func ("usage: %s COMMAND [options]\n",
-              argv[0]);
-  print_func ("Builtin commands:\n");
-
-  while (builtin->name)
-    {
-      print_func ("  %s\n", builtin->name);
-      builtin++;
-    }
-  return (is_error ? 1 : 0);
-}
-
-
-int
-main (int    argc,
-      char **argv)
-{
-  OsbuildBuiltin *builtin;
-  const char *cmd;
-
-  g_type_init ();
-
-  g_set_prgname (argv[0]);
-
-  builtin = builtins;
-
-  if (argc < 2)
-    return usage (argv, 1);
-  
-  cmd = argv[1];
-
-  while (builtin->name)
-    {
-      GError *error = NULL;
-      if (strcmp (cmd, builtin->name) == 0)
-        {
-          int i;
-          int tmp_argc;
-          char **tmp_argv;
-
-          tmp_argc = argc - 1;
-          tmp_argv = g_new0 (char *, tmp_argc + 1);
-
-          tmp_argv[0] = (char*)builtin->name;
-          for (i = 0; i < tmp_argc; i++)
-            tmp_argv[i+1] = argv[i+2];
-          if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
-            {
-              g_free (tmp_argv);
-              g_printerr ("%s\n", error->message);
-              g_clear_error (&error);
-              return 1;
-            }
-          g_free (tmp_argv);
-          return 0;
-        }
-      builtin++;
-    }
-  
-  g_printerr ("Unknown command '%s'\n", cmd);
-  return usage (argv, 1);
-}
diff --git a/osbuild/ob-builtin-buildone.c b/osbuild/ob-builtin-buildone.c
deleted file mode 100644 (file)
index 1387563..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "otutil.h"
-#include "ob-builtins.h"
-
-#include <glib/gi18n.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-static char *repo_path;
-static char *ref;
-static char *name;
-static char *generator;
-static char *resultdir;
-static gboolean raw;
-
-static GOptionEntry options[] = {
-  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
-  { "rev", 'r', 0, G_OPTION_ARG_STRING, &ref, "Build using this tree", "rev" },
-  { "name", 0, 0, G_OPTION_ARG_STRING, &name, "Name of the source", "source" },
-  { "generator", 0, 0, G_OPTION_ARG_FILENAME, &generator, "Script to run on installed tree", "script" },
-  { "raw", 0, 0, G_OPTION_ARG_NONE, &raw, "Do not instantiate a tree, use current", NULL },
-  { "resultdir", 0, 0, G_OPTION_ARG_FILENAME, &resultdir, "Directory for output artifacts", "dir" },
-  { NULL }
-};
-
-static char *
-get_tmpdir (void) G_GNUC_UNUSED;
-
-static char *
-get_tmpdir (void)
-{
-  char *tmp_prefix = g_strdup (g_getenv ("XDG_RUNTIME_DIR"));
-  char *ret;
-  
-  if (tmp_prefix)
-    {
-      ret = g_strdup_printf ("%s/osbuild", tmp_prefix);
-    }
-  else
-    {
-      tmp_prefix = g_strdup_printf ("/tmp/osbuild-%d", getuid ());
-      if (!g_file_test (tmp_prefix, G_FILE_TEST_IS_DIR))
-        {
-          if (!mkdir (tmp_prefix, 0755))
-            {
-              g_printerr ("Failed to make logging directory %s\n", tmp_prefix);
-              exit (1);
-            }
-        }
-      ret = tmp_prefix;
-      tmp_prefix = NULL;
-    }
-  g_free (tmp_prefix);
-  return ret;
-}
-
-static gboolean
-open_log (const char *name, 
-          GOutputStream **out_log,
-          GError **error) G_GNUC_UNUSED;
-
-static gboolean
-open_log (const char *name, 
-          GOutputStream **out_log,
-          GError **error)
-{
-  gboolean ret = FALSE;
-  char *tmpdir = NULL;
-  char *path = NULL;
-  GFile *logf = NULL;
-  GFileOutputStream *ret_log = NULL;
-
-  path = g_strdup_printf ("%s/%s.log", tmpdir, name);
-  logf = ot_util_new_file_for_path (path);
-
-  ret_log = g_file_replace (logf, NULL, FALSE, 0, NULL, error);
-  if (!ret_log)
-    goto out;
-
-  ret = TRUE;
-  *out_log = (GOutputStream*)ret_log;
-  ret_log = NULL;
- out:
-  g_free (path);
-  g_free (tmpdir);
-  g_clear_object (&logf);
-  g_clear_object (&ret_log);
-  return ret;
-}
-
-gboolean
-osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-
-  context = g_option_context_new ("- Build current directory");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (!raw && !repo_path)
-    {
-      ot_util_usage_error (context, "A result directory must be specified with --resultdir", error);
-      goto out;
-    }
-
-  if (!generator)
-    generator = g_build_filename (LIBEXECDIR, "ostree", "generators", "default", NULL);
-
-  
-
- out:
-  if (context)
-    g_option_context_free (context);
-  return ret;
-}
diff --git a/osbuild/ob-builtins.h b/osbuild/ob-builtins.h
deleted file mode 100644 (file)
index 800039b..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSBUILD_BUILTINS__
-#define __OSBUILD_BUILTINS__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef enum {
-  OSBUILD_BUILTIN_FLAG_NONE = 0,
-} OsbuildBuiltinFlags;
-
-typedef struct {
-  const char *name;
-  gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
-  int flags; /* OsbuildBuiltinFlags */
-} OsbuildBuiltin;
-
-gboolean osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error);
-
-G_END_DECLS
-
-#endif
diff --git a/osbuild/osbuild-raw-makeinstall.c b/osbuild/osbuild-raw-makeinstall.c
deleted file mode 100644 (file)
index a404ffe..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include "otutil.h"
-
-#include <string.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-static const char *
-find_first_file (GFileTest test, const char *name, ...) G_GNUC_NULL_TERMINATED;
-
-static const char *
-find_first_file (GFileTest test, const char *name, ...)
-{
-  va_list args;
-
-  va_start (args, name);
-
-  do
-    {
-      if (g_file_test (name, test))
-        break;
-      name = va_arg (args, const char *);
-    }
-  while (name != NULL);
-
-  va_end (args);
-
-  return name;
-}
-
-static void
-split_configure_make_args (int argc,
-                           char **argv,
-                           GPtrArray **out_configure_args,
-                           GPtrArray **out_make_args,
-                           GPtrArray **out_makeinstall_args)
-{
-  int i;
-
-  *out_configure_args = g_ptr_array_new ();
-  *out_make_args = g_ptr_array_new ();
-  *out_makeinstall_args = g_ptr_array_new ();
-
-  for (i = 1; i < argc; i++)
-    {
-      if (g_str_has_prefix (argv[i], "--"))
-        g_ptr_array_add (*out_configure_args, argv[i]);
-      else if (g_str_has_prefix (argv[i], "DESTDIR="))
-        g_ptr_array_add (*out_makeinstall_args, argv[i]);
-      else
-        g_ptr_array_add (*out_make_args, argv[i]);
-    }
-}
-
-static void
-spawn_sync_or_fatal (char **args, char **env, GSpawnFlags flags)
-{
-  GError *error = NULL;
-  int estatus;
-  char **iter;
-
-  g_print ("osbuild: running: ");
-  for (iter = args; *iter; iter++)
-    g_print ("%s ", *iter);
-  g_print ("\n");
-  if (g_spawn_sync (NULL, args, env, flags, NULL, NULL, NULL, NULL, &estatus, &error))
-    { 
-      if (WIFEXITED (estatus) && WEXITSTATUS (estatus) == 0)
-        {
-          g_message ("Subprocess %s exited successfully\n", args[0]);
-        }
-      else
-        {
-          if (WIFEXITED (estatus))
-            g_error ("Subprocess %s exited with code %d\n", args[0], WEXITSTATUS (estatus));
-          else if (WIFSIGNALED (estatus))
-            g_error ("Subprocess %s killed by signal %d\n", args[0], WTERMSIG (estatus));
-          else
-            g_error ("Subprocess %s terminated with status %d\n", args[0], estatus);
-          exit (1);
-        }
-    }
-  else
-    {
-      g_error ("Failed to execute %s: %s\n", args[0], error->message);
-      exit (1);
-    }
-}
-
-static void
-ptr_array_extend (GPtrArray *dest, GPtrArray *to_append)
-{
-  int i;
-
-  for (i = 0; i < to_append->len; i++)
-    g_ptr_array_add (dest, to_append->pdata[i]);
-}
-
-int
-main (int    argc,
-      char **argv)
-{
-  GPtrArray *config_args;
-  GPtrArray *make_args;
-  GPtrArray *makeinstall_args;
-  GPtrArray *args;
-  char **subprocess_env;
-
-  g_type_init ();
-
-  g_set_prgname (argv[0]);
-
-  args = g_ptr_array_new ();
-
-  subprocess_env = g_get_environ ();
-  ot_g_environ_setenv (subprocess_env, "LANG", "C", TRUE);
-  ot_g_environ_unsetenv (subprocess_env, "LC_ALL");
-
-  split_configure_make_args (argc, argv, &config_args, &make_args, &makeinstall_args);
-
-  if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
-    {
-      const char *autogen;
-      char **autogen_env;
-      
-      autogen = find_first_file (G_FILE_TEST_IS_EXECUTABLE, "./autogen", "./autogen.sh", NULL);
-      if (!autogen)
-        ot_util_fatal_literal ("No executable configure or autogen script found"); 
-
-      autogen_env = g_strdupv (subprocess_env);
-      ot_g_environ_setenv (autogen_env, "NOCONFIGURE", "1", TRUE);
-      
-      g_ptr_array_set_size (args, 0);
-      g_ptr_array_add (args, (char*) autogen);
-      g_ptr_array_add (args, NULL);
-      spawn_sync_or_fatal ((char**)args->pdata, autogen_env, 0);
-    }
-
-  if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
-    ot_util_fatal_literal ("autogen script failed to generate a configure script");
-
-  g_ptr_array_set_size (args, 0);
-  g_ptr_array_add (args, "./configure");
-  ptr_array_extend (args, config_args);
-  g_ptr_array_add (args, NULL);
-  spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, 0);
-    
-  g_ptr_array_set_size (args, 0);
-  g_ptr_array_add (args, "make");
-  ptr_array_extend (args, make_args);
-  g_ptr_array_add (args, NULL);
-  spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
-  
-  g_ptr_array_set_size (args, 0);
-  g_ptr_array_add (args, "make");
-  g_ptr_array_add (args, "install");
-  ptr_array_extend (args, makeinstall_args);
-  g_ptr_array_add (args, NULL);
-  spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
-
-  return 0;
-}
diff --git a/osbuild/ostree-buildone b/osbuild/ostree-buildone
deleted file mode 100644 (file)
index 2f2fc6d..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-#!/usr/bin/python
-#
-# ostree-buildone:
-# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
-# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
-
-# The build output is automatically logged to $TMPDIR/build-$(PWD).log.
-# For example, invoking metabuild in a directory named "foo" will log
-# to /tmp/build-foo.log
-#
-# You can pass arguments to metabuild; if they start with '--', they're
-# given to configure.  Otherwise, they're passed to make.
-#
-# $ metabuild --enable-libfoo  # passed to configure
-# $ metabuild -j 1             # passed to make
-
-import os,sys,subprocess,tempfile,re
-import select,time,stat,fcntl
-
-subprocess_nice_args = []
-
-# In the future we should test for this better; possibly implement a
-# custom fork handler
-try:
-    chrt_args = ['chrt', '--idle', '0']
-    proc = subprocess.Popen(chrt_args + ['true'])
-    if proc.wait() == 0:
-        subprocess_nice_args.extend(chrt_args)
-except OSError, e:
-    pass
-
-try:
-    ionice_args = ['ionice', '-c', '3', '-t']
-    proc = subprocess.Popen(ionice_args + ['true'])
-    if proc.wait() == 0:
-        subprocess_nice_args.extend(ionice_args)
-except OSError, e:
-    pass
-
-warning_re = re.compile(r'(: ((warning)|(error)|(fatal error)): )|(make(\[[0-9]+\])?: \*\*\*)')
-output_whitelist_re = re.compile(r'^(make(\[[0-9]+\])?: Entering directory)|(ostree-build )')
-
-_bold_sequence = None
-_normal_sequence = None
-if os.isatty(1):
-    _bold_sequence = subprocess.Popen(['tput', 'bold'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
-    _normal_sequence = subprocess.Popen(['tput', 'sgr0'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
-def _bold(text):
-    if _bold_sequence is not None:
-        return '%s%s%s' % (_bold_sequence, text, _normal_sequence)
-    else:
-        return text
-
-class Mainloop(object):
-    DEFAULT = None
-    def __init__(self):
-        self._running = True
-        self.poll = select.poll()
-        self._timeouts = []
-        self._pid_watches = {}
-        self._fd_callbacks = {}
-
-    @classmethod
-    def get(cls, context):
-        if context is None:
-            if cls.DEFAULT is None:
-                cls.DEFAULT = cls()
-            return cls.DEFAULT
-        raise NotImplementedError("Unknown context %r" % (context, ))
-
-    def watch_fd(self, fd, callback):
-        self.poll.register(fd)
-        self._fd_callbacks[fd] = callback
-
-    def unwatch_fd(self, fd):
-        self.poll.unregister(fd)
-        del self._fd_callbacks[fd]
-
-    def watch_pid(self, pid, callback):
-        self._pid_watches[pid] = callback
-
-    def timeout_add(self, ms, callback):
-        self._timeouts.append((ms, callback))
-
-    def quit(self):
-        self._running = False
-
-    def run_once(self):
-        min_timeout = None
-        if len(self._pid_watches) > 0:
-            min_timeout = 500
-        for (ms, callback) in self._timeouts:
-            if (min_timeout is None) or (ms < min_timeout):
-                min_timeout = ms
-        origtime = time.time() * 1000
-        fds = self.poll.poll(min_timeout)
-        for fd in fds:
-            self._fd_callbacks[fd]()
-        for pid in self._pid_watches:
-            (opid, status) = os.waitpid(pid, os.WNOHANG)
-            if opid != 0:
-                self._pid_watches[pid](opid, status)
-                del self._pid_watches[pid]
-        newtime = time.time() * 1000
-        diff = int(newtime - origtime)
-        if diff < 0: diff = 0
-        for i,(ms, callback) in enumerate(self._timeouts):
-            remaining_ms = ms - diff
-            if remaining_ms <= 0:
-                callback()
-            else:
-                self._timeouts[i] = (remaining_ms, callback)
-
-    def run(self):
-        while self._running:
-            self.run_once()
-
-class FileMonitor(object):
-    def __init__(self):
-        self._paths = {}
-        self._path_modtimes = {}
-        self._timeout = 1000
-        self._timeout_installed = False
-        self._loop = Mainloop.get(None)
-
-    def _stat(self, path):
-        try:
-            st = os.stat(path)
-            return st[stat.ST_MTIME]
-        except OSError, e:
-            return None
-
-    def add(self, path, callback):
-        if path not in self._paths:
-            self._paths[path] = []
-            self._path_modtimes[path] = self._stat(path)
-        self._paths[path].append(callback)
-        if not self._timeout_installed:
-            self._timeout_installed = True
-            self._loop.timeout_add(self._timeout, self._check_files)
-
-    def _check_files(self):
-        for (path,callbacks) in self._paths.iteritems():
-            mtime = self._stat(path)
-            orig_mtime = self._path_modtimes[path]
-            if (mtime is not None) and (orig_mtime is None or (mtime > orig_mtime)):
-                self._path_modtimes[path] = mtime
-                for cb in callbacks:
-                    cb()
-
-_filemon = FileMonitor()
-
-class OutputFilter(object):
-    def __init__(self, filename, output):
-        self.filename = filename
-        self.output = output
-
-        # inherit globals
-        self._warning_re = warning_re
-        self._nonfilter_re = output_whitelist_re
-
-        self._buf = ''
-        self._warning_count = 0
-        self._filtered_line_count = 0
-        _filemon.add(filename, self._on_changed)
-        self._fd = os.open(filename, os.O_RDONLY)
-        fcntl.fcntl(self._fd, fcntl.F_SETFL, os.O_NONBLOCK)
-
-    def _do_read(self):
-        while True:
-            buf = os.read(self._fd, 4096)
-            if buf == '':
-                break
-            self._buf += buf
-        self._flush()
-
-    def _write_last_log_lines(self):
-        _last_line_limit = 100
-        f = open(logfile_path)
-        lines = []
-        for line in f:
-            if line.startswith('ostree-build '):
-                continue
-            lines.append(line)
-            if len(lines) > _last_line_limit:
-                lines.pop(0)
-        f.close()
-        for line in lines:
-            self.output.write('| ')
-            self.output.write(line)
-
-    def _flush(self):
-        while True:
-            p = self._buf.find('\n')
-            if p < 0:
-                break
-            line = self._buf[0:p]
-            self._buf = self._buf[p+1:]
-            match = self._warning_re.search(line)
-            if match:
-                self._warning_count += 1
-                self.output.write(line + '\n')
-            else:    
-                match = self._nonfilter_re.search(line)
-                if match:
-                    self.output.write(line + '\n')
-                else:
-                    self._filtered_line_count += 1
-
-    def _on_changed(self):
-        self._do_read()
-
-    def start(self):
-        self._do_read()
-
-    def finish(self, successful):
-        self._do_read()
-        if not successful:
-            self._write_last_log_lines()
-            pass
-        self.output.write("ostree-build %s: %d warnings\n" % ('success' if successful else _bold('failed'),
-                                                            self._warning_count, ))
-        self.output.write("ostree-build: full log path: %s\n" % (logfile_path, ))
-        
-        if successful:
-            for f in os.listdir('_build'):
-                path = os.path.join('_build', f)
-                if f.startswith('artifact-'):
-                    self.output.write("ostree-build: created artifact: %s\n" % (f, ))
-        sys.exit(0 if successful else 1)
-
-def _on_makeinstall_exit(pid, estatus):
-    _output_filter.finish(estatus == 0)
-
-def _on_make_exit(pid, estatus):
-    if estatus == 0:
-        args = list(subprocess_nice_args)
-        args.append('ostree-buildone-makeinstall-split-artifacts')
-        _logfile_f.write("Running: %r\n" % (args, ))
-        _logfile_f.flush()
-        proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
-        _loop.watch_pid(proc.pid, _on_makeinstall_exit)
-    else:
-        _output_filter.finish(False)
-
-def _get_version():
-    if not os.path.isdir('.git'):
-        sys.stderr.write("ostree-buildone: error: Couldn't find .git directory")
-        sys.exit(1)
-
-    proc = subprocess.Popen(['git', 'describe'], stdout=subprocess.PIPE)
-    output = proc.communicate()[0].strip()
-    if proc.wait() != 0:
-        proc = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE)
-        if proc.wait() != 0:
-            sys.stderr.write("ostree-buildone: error: git rev-parse HEAD failed")
-            sys.exit(1)
-        output = proc.communicate()[0].strip()
-    return output
-
-if __name__ == '__main__':
-    user_tmpdir = os.environ.get('XDG_RUNTIME_DIR')
-    if user_tmpdir is None:
-        user_tmpdir = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'metabuild-%s' % (os.getuid(), ))
-    else:
-        user_tmpdir = os.path.join(user_tmpdir, 'ostree-build') 
-
-    os.environ['OSBUILD_VERSION'] = _get_version()
-
-    if os.path.isdir('_build'):
-        for filename in os.listdir('_build'):
-            path = os.path.join('_build', filename)
-            if filename.startswith('artifact-'):
-                os.unlink(path)
-
-    if not os.path.isdir(user_tmpdir):
-        os.makedirs(user_tmpdir)
-    logfile_path = os.path.join(user_tmpdir, '%s.log' % (os.path.basename(os.getcwd()), ))
-    try:
-        os.unlink(logfile_path)
-    except OSError, e:
-        pass
-    logfile_write_fd = os.open(logfile_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
-    global _logfile_f
-    _logfile_f = os.fdopen(logfile_write_fd, "w")
-    sys.stdout.write('ostree-build: logging to %r\n' % (logfile_path, ))
-    sys.stdout.flush()
-
-    global _output_filter
-    _output_filter = OutputFilter(logfile_path, sys.stdout)
-    _output_filter.start()
-
-    args = list(subprocess_nice_args)
-    args.append('ostree-buildone-make')
-    args.extend(sys.argv[1:])
-    devnull=open('/dev/null')
-    _logfile_f.write("Running: %r\n" % (args, ))
-    _logfile_f.flush()
-    proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
-
-    global _loop
-    _loop = Mainloop.get(None)
-    _loop.watch_pid(proc.pid, _on_make_exit)
-    _loop.run()
diff --git a/osbuild/ostree-buildone-make b/osbuild/ostree-buildone-make
deleted file mode 100644 (file)
index b6e89f5..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-#!/usr/bin/python
-
-# ostree-buildone-raw: Generic build system wrapper
-# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
-# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
-
-# ostree-buildone-raw wraps systems that implement the GNOME build API:
-# http://people.gnome.org/~walters/docs/build-api.txt
-
-import os,sys,subprocess,tempfile,re
-from multiprocessing import cpu_count
-import select,time
-
-root = None
-
-prefix = '/usr'
-
-# libdir detection
-if os.path.isdir('/lib64'):
-    libdir=os.path.join(prefix, 'lib64')
-else:
-    libdir=os.path.join(prefix, 'lib')
-
-default_buildapi_jobs = ['-j', '%d' % (cpu_count() * 2, )]
-configargs = ['--prefix=' + prefix,
-              '--libdir=' + libdir,
-              '--sysconfdir=/etc',
-              '--localstatedir=/var',
-              '--bindir=' + os.path.join(prefix, 'bin'),
-              '--sbindir=' + os.path.join(prefix, 'sbin'),
-              '--datadir=' + os.path.join(prefix, 'share'),
-              '--includedir=' + os.path.join(prefix, 'include'),
-              '--libexecdir=' + os.path.join(prefix, 'libexec'),
-              '--mandir=' + os.path.join(prefix, 'share', 'man'),
-              '--infodir=' + os.path.join(prefix, 'share', 'info')]
-makeargs = ['make']
-
-top_srcdir=os.getcwd()
-
-for arg in sys.argv[1:]:
-    if arg.startswith('--'):
-        configargs.append(arg)
-    else:
-        makeargs.append(arg)
-
-def log(msg):
-    fullmsg = 'ostree-buildone: ' + msg + '\n'
-    sys.stdout.write(fullmsg)
-    sys.stdout.flush()
-
-def fatal(msg):
-    log(msg)
-    sys.exit(1)
-
-def run_sync(args, env=None):
-    log("Running: %r" % (args, ))
-    f = open('/dev/null', 'r')
-    proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
-                            close_fds=True, env=env)
-    f.close()
-    returncode = proc.wait()
-    log("pid %d exited with code %d" % (proc.pid, returncode))
-    if returncode != 0:
-        sys.exit(1)
-
-class BuildSystemScanner(object):
-    @classmethod
-    def _find_file(cls, names):
-        for name in names:
-            if os.path.exists(name):
-                return name
-        return None
-
-    @classmethod
-    def get_configure_source_script(cls):
-        return cls._find_file(('./configure.ac', './configure.in'))
-
-    @classmethod
-    def get_configure_script(cls):
-        return cls._find_file(('./configure', ))
-
-    @classmethod
-    def get_bootstrap_script(cls):
-        return cls._find_file(('./autogen.sh', ))
-
-    @classmethod
-    def get_silent_rules(cls):
-        src = cls.get_configure_source_script()
-        if not src:
-            return False
-        f = open(src)
-        for line in f:
-            if line.find('AM_SILENT_RULES') >= 0:
-                f.close()
-                return True
-        f.close()
-        return False
-
-def _search_file(filename, pattern):
-    f = open(filename)
-    for line in f:
-        if line.startswith(pattern):
-            f.close()
-            return line
-    f.close()
-    return None
-
-def _find_buildapi_makevariable(name):
-    var = '.%s:' % (name, )
-    line = None
-    if os.path.exists('Makefile.in'):
-        line = _search_file('Makefile.in', var)
-    if not line and os.path.exists('Makefile'):
-        line = _search_file('Makefile', var)
-    return line is not None
-
-def phase_bootstrap():        
-    have_configure = BuildSystemScanner.get_configure_script() 
-    have_configure_source = BuildSystemScanner.get_configure_source_script()
-    if not (have_configure or have_configure_source):
-        fatal("No configure or bootstrap script detected; unknown buildsystem")
-        return
-
-    need_v1 = BuildSystemScanner.get_silent_rules()
-    if need_v1:
-        log("Detected AM_SILENT_RULES, adding --disable-silent-rules to configure")
-        configargs.append('--disable-silent-rules')
-
-    if have_configure:
-        phase_configure()
-    else:
-        bootstrap = BuildSystemScanner.get_bootstrap_script()
-        if bootstrap:
-            log("Detected bootstrap script: %s, using it" % (bootstrap, ))
-            args = [bootstrap]
-            args.extend(configargs)
-            # Add NOCONFIGURE; GNOME style scripts use this
-            env = dict(os.environ)
-            env['NOCONFIGURE'] = '1'
-            run_sync(args, env=env)
-        else:
-            log("No bootstrap script found; using generic autoreconf")
-            run_sync(['autoreconf', '-f', '-i'])
-        phase_configure()
-
-def phase_configure():
-    use_builddir = True
-    doesnot_support_builddir = _find_buildapi_makevariable('buildapi-no-builddir')
-    if doesnot_support_builddir:
-        log("Found .buildapi-no-builddir; copying source tree to _build")
-        shutil.rmtree('_build')
-        os.mkdir('_build')
-        shutil.copytree('.', '_build', symlinks=True,
-                        ignore=shutil.ignore_patterns('_build'))
-        use_builddir = False
-        builddir = '.'
-    else:
-        builddir = '_build'
-
-    if not use_builddir:
-        configdir = './'
-    else:
-        configdir = os.getcwd()
-        builddir = builddir
-        log("Using build directory %r" % (builddir, ))
-        if not os.path.isdir(builddir):
-            os.mkdir(builddir)
-        os.chdir(builddir)
-
-    configstatus = 'config.status'
-    if not os.path.exists(configstatus):
-        args = [os.path.join(configdir, 'configure')]
-        args.extend(configargs)
-        run_sync(args)
-    else:
-        log("Found %s, skipping configure" % (configstatus, ))
-    phase_build()
-
-build_status = False
-
-def phase_build():
-    if not os.path.exists('Makefile'):
-        log("No Makefile found")
-        sys.exit(1)
-    args = makeargs
-    user_specified_jobs = False
-    for arg in args:
-        if arg == '-j':
-            user_specified_jobs = True
-
-    if not user_specified_jobs:
-        notparallel = _find_buildapi_makevariable('NOTPARALLEL')
-        if not notparallel:
-            log("Didn't find NOTPARALLEL, using parallel make by default")
-            args.extend(default_buildapi_jobs)
-
-    run_sync(args)
-
-def phase_complete():
-    sys.exit(0)
-
-log("invocation arguments: %r" % (sys.argv, ))
-
-# Start off the process
-phase_bootstrap()
diff --git a/osbuild/ostree-buildone-makeinstall-split-artifacts b/osbuild/ostree-buildone-makeinstall-split-artifacts
deleted file mode 100644 (file)
index 85167a5..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/python
-
-# ostree-buildone-raw: Generic build system wrapper
-# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
-# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
-
-import os,sys,re,subprocess
-import tempfile,shutil
-
-_devel_regexps = map(re.compile,
-                     [r'/usr/include/',
-                      r'/usr/share/pkgconfig/',
-                      r'/.*lib(?:|(?:32)|(?:64))/pkgconfig/.*\.pc',
-                      r'/.*lib(?:|(?:32)|(?:64))/.*\.so$'])
-
-def log(msg):
-    fullmsg = 'ostree-buildone: ' + msg + '\n'
-    sys.stdout.write(fullmsg)
-    sys.stdout.flush()
-
-tempfiles = []
-
-def do_exit(code):
-    for tmpname in tempfiles:
-        if os.path.isdir(tmpname):
-            shutil.rmtree(tmpname)
-        else:
-            try:
-                os.unlink(tmpname)
-                pass
-            except OSError, e:
-                pass
-    sys.exit(code)
-
-def fatal(msg):
-    log(msg)
-    do_exit(1)
-
-def run_sync(args, env=None):
-    log("Running: %r" % (args, ))
-    f = open('/dev/null', 'r')
-    proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
-                            close_fds=True, env=env)
-    f.close()
-    returncode = proc.wait()
-    if returncode == 0:
-        func = log
-    else:
-        func = fatal
-    func("pid %d exited with code %d" % (proc.pid, returncode))
-
-basename=os.path.basename(os.getcwd())
-artifact_prefix='artifact-%s,%s' % (basename, os.environ['OSBUILD_VERSION'])
-origdir=os.getcwd()
-os.chdir('_build')
-
-if not os.path.exists('Makefile'):
-    log("No Makefile found")
-    do_exit(1)
-
-(fd,fakeroot_temp)=tempfile.mkstemp(prefix='ostree-fakeroot-%s-' % (basename,))
-os.close(fd)
-tempfiles.append(fakeroot_temp)
-tempdir = tempfile.mkdtemp(prefix='ostree-build-%s-' % (basename,))
-tempfiles.append(tempdir)
-args = ['fakeroot', '-s', fakeroot_temp, 'make', 'install', 'DESTDIR=' + tempdir]
-run_sync(args)
-
-devel_files = set()
-runtime_files = set()
-
-oldpwd=os.getcwd()
-os.chdir(tempdir)
-for root, dirs, files in os.walk('.'):
-    for filename in files:
-        path = os.path.join(root, filename)
-        matched = False
-        for r in _devel_regexps:
-            if r.match(path[1:]):
-                devel_files.add(path)
-                matched = True
-                break
-        if not matched:    
-            runtime_files.add(path)
-os.chdir(oldpwd)
-
-def make_artifact(name, from_files):
-    artifact_target = '%s-%s.tar.gz' % (artifact_prefix, name)
-    (fd,filelist_temp)=tempfile.mkstemp(prefix='ostree-filelist-%s-%s' % (basename, name))
-    os.close(fd)
-    tempfiles.append(filelist_temp)
-    f = open(filelist_temp, 'w')
-    for filename in from_files:
-        f.write(filename)
-        f.write('\n')
-    f.close()
-    args = ['fakeroot', '-i', fakeroot_temp, 'tar', '-c', '-z', '-C', tempdir, '-f', artifact_target, '-T', filelist_temp]
-    run_sync(args)
-
-if devel_files:
-    make_artifact('devel', devel_files)
-make_artifact('runtime', runtime_files)
-
-do_exit(0)
diff --git a/ostree/main.c b/ostree/main.c
deleted file mode 100644 (file)
index cf0e89c..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include <gio/gio.h>
-
-#include <string.h>
-
-#include "ot-builtins.h"
-
-static OstreeBuiltin builtins[] = {
-  { "checkout", ostree_builtin_checkout, 0 },
-  { "diff", ostree_builtin_diff, 0 },
-  { "init", ostree_builtin_init, 0 },
-  { "commit", ostree_builtin_commit, 0 },
-  { "compose", ostree_builtin_compose, 0 },
-  { "log", ostree_builtin_log, 0 },
-#ifdef HAVE_LIBSOUP_GNOME
-  { "pull", ostree_builtin_pull, 0 },
-#endif
-  { "fsck", ostree_builtin_fsck, 0 },
-  { "remote", ostree_builtin_remote, 0 },
-  { "rev-parse", ostree_builtin_rev_parse, 0 },
-  { "remote", ostree_builtin_remote, 0 },
-  { "run-triggers", ostree_builtin_run_triggers, 0 },
-  { "show", ostree_builtin_show, 0 },
-  { NULL }
-};
-
-static int
-usage (char **argv, gboolean is_error)
-{
-  OstreeBuiltin *builtin = builtins;
-  void (*print_func) (const gchar *format, ...);
-
-  if (is_error)
-    print_func = g_printerr;
-  else
-    print_func = g_print;
-
-  print_func ("usage: %s --repo=PATH COMMAND [options]\n",
-              argv[0]);
-  print_func ("Builtin commands:\n");
-
-  while (builtin->name)
-    {
-      print_func ("  %s\n", builtin->name);
-      builtin++;
-    }
-  return (is_error ? 1 : 0);
-}
-
-
-int
-main (int    argc,
-      char **argv)
-{
-  OstreeBuiltin *builtin;
-  const char *cmd;
-  const char *repo;
-
-  g_type_init ();
-
-  g_set_prgname (argv[0]);
-
-  builtin = builtins;
-
-  if (argc < 3)
-    return usage (argv, 1);
-  
-  if (!g_str_has_prefix (argv[1], "--repo="))
-    return usage (argv, 1);
-  repo = argv[1] + strlen ("--repo=");
-
-  cmd = argv[2];
-
-  while (builtin->name)
-    {
-      GError *error = NULL;
-      if (strcmp (cmd, builtin->name) == 0)
-        {
-          int i;
-          int tmp_argc;
-          char **tmp_argv;
-
-          tmp_argc = argc - 2;
-          tmp_argv = g_new0 (char *, tmp_argc + 1);
-
-          tmp_argv[0] = (char*)builtin->name;
-          for (i = 0; i < tmp_argc; i++)
-            tmp_argv[i+1] = argv[i+3];
-          if (!builtin->fn (tmp_argc, tmp_argv, repo, &error))
-            {
-              g_free (tmp_argv);
-              g_printerr ("%s\n", error->message);
-              g_clear_error (&error);
-              return 1;
-            }
-          g_free (tmp_argv);
-          return 0;
-        }
-      builtin++;
-    }
-  
-  g_printerr ("Unknown command '%s'\n", cmd);
-  return usage (argv, 1);
-}
diff --git a/ostree/ot-builtin-checkout.c b/ostree/ot-builtin-checkout.c
deleted file mode 100644 (file)
index 90493fc..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
-  { NULL }
-};
-
-gboolean
-ostree_builtin_checkout (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  OstreeCheckout *checkout = NULL;
-  const char *commit;
-  const char *destination;
-  GFile *destf = NULL;
-
-  context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 3)
-    {
-      gchar *help = g_option_context_get_help (context, TRUE, NULL);
-      g_printerr ("%s\n", help);
-      g_free (help);
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "COMMIT and DESTINATION must be specified");
-      goto out;
-    }
-  
-  commit = argv[1];
-  destination = argv[2];
-  
-  if (!ostree_repo_checkout (repo, commit, destination, NULL, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  g_clear_object (&checkout);
-  g_clear_object (&destf);
-  return ret;
-}
diff --git a/ostree/ot-builtin-commit.c b/ostree/ot-builtin-commit.c
deleted file mode 100644 (file)
index 3d4d6b9..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <gio/gunixoutputstream.h>
-
-#include <glib/gi18n.h>
-
-static gboolean separator_null;
-static int from_fd = -1;
-static gboolean from_stdin;
-static char *from_file;
-static char *metadata_text_path;
-static char *metadata_bin_path;
-static char *subject;
-static char *body;
-static char *parent;
-static char *branch;
-
-static GOptionEntry options[] = {
-  { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
-  { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
-  { "metadata-variant-text", 0, 0, G_OPTION_ARG_FILENAME, &metadata_text_path, "File containing g_variant_print() output", "path" },
-  { "metadata-variant", 0, 0, G_OPTION_ARG_FILENAME, &metadata_bin_path, "File containing serialized variant, in host endianness", "path" },
-  { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
-  { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
-  { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
-  { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
-  { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
-  { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
-  { NULL }
-};
-
-typedef struct {
-  GFile *dir;
-  char separator;
-  GOutputStream *out;
-  GCancellable *cancellable;
-} FindThreadData;
-
-static gboolean
-find (const char *basepath,
-      GFile *dir,
-      char separator,
-      GOutputStream *out,
-      GCancellable *cancellable,
-      GError  **error);
-
-static gboolean
-find_write_child (const char *basepath,
-                  GFile      *dir,
-                  char        separator,
-                  GOutputStream *out,
-                  GFileInfo  *finfo,
-                  GCancellable *cancellable,
-                  GError    **error)
-{
-  gboolean ret = FALSE;
-  guint32 type;
-  const char *name;
-  char buf[1];
-  char *child_path = NULL;
-  GString *child_trimmed_path = NULL;
-  GFile *child = NULL;
-  gsize bytes_written;
-
-  type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
-  name = g_file_info_get_attribute_byte_string (finfo, "standard::name");
-
-  child = g_file_get_child (dir, name);
-
-  if (type == G_FILE_TYPE_DIRECTORY)
-    {
-      if (!find (basepath, child, separator, out, cancellable, error))
-        goto out;
-    }
-
-  child_path = g_file_get_path (child);
-  child_trimmed_path = g_string_new (child_path + strlen (basepath));
-  if (!*(child_trimmed_path->str))
-    {
-      /* do nothing - we implicitly add the root . */
-    }
-  else
-    {
-      g_assert (*(child_trimmed_path->str) == '/');
-      g_string_insert_c (child_trimmed_path, 0, '.');
-
-      if (!g_output_stream_write_all (out, child_trimmed_path->str, child_trimmed_path->len,
-                                      &bytes_written, cancellable, error))
-        goto out;
-      buf[0] = separator;
-      if (!g_output_stream_write_all (out, buf, 1, &bytes_written,
-                                      cancellable, error))
-        goto out;
-    }
-      
-  ret = TRUE;
- out:
-  g_string_free (child_trimmed_path, TRUE);
-  child_trimmed_path = NULL;
-  g_free (child_path);
-  child_path = NULL;
-  g_clear_object (&child);
-  return ret;
-}
-
-static gboolean
-find (const char *basepath,
-      GFile *dir,
-      char separator,
-      GOutputStream *out,
-      GCancellable *cancellable,
-      GError  **error)
-{
-  gboolean ret = FALSE;
-  GError *temp_error = NULL;
-  GFileEnumerator *enumerator = NULL;
-  GFileInfo *finfo = NULL;
-
-  enumerator = g_file_enumerate_children (dir, "standard::type,standard::name", 
-                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                          cancellable, error);
-  if (!enumerator)
-    goto out;
-
-  while ((finfo = g_file_enumerator_next_file (enumerator, cancellable, error)) != NULL)
-    {
-      if (!find_write_child (basepath, dir, separator, out, finfo, cancellable, error))
-        goto out;
-      g_clear_object (&finfo);
-    }
-  if (temp_error)
-    {
-      g_propagate_error (error, temp_error);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  g_clear_object (&finfo);
-  g_clear_object (&enumerator);
-  return ret;
-}
-
-static gpointer
-find_thread (gpointer data)
-{
-  FindThreadData *tdata = data;
-  GError *error = NULL;
-  char *path;
-  
-  path = g_file_get_path (tdata->dir);
-  if (!find (path, tdata->dir, tdata->separator, tdata->out,
-             tdata->cancellable, &error))
-    {
-      g_printerr ("%s", error->message);
-      g_clear_error (&error);
-    }
-  g_free (path);
-  g_clear_object (&(tdata->dir));
-  g_clear_object (&(tdata->out));
-  return NULL;
-}
-
-gboolean
-ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  char *dir = NULL;
-  GChecksum *commit_checksum = NULL;
-  char separator;
-  GVariant *metadata = NULL;
-  GMappedFile *metadata_mappedf = NULL;
-  GFile *metadata_f = NULL;
-  gboolean temp_fd = -1;
-  int pipefd[2] = { -1, -1 };
-  GOutputStream *out = NULL;
-  FindThreadData fdata;
-
-  context = g_option_context_new ("[DIR] - Commit a new revision");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (argc > 1)
-    dir = g_strdup (argv[1]);
-  else
-    dir = g_get_current_dir ();
-
-  if (g_str_has_suffix (dir, "/"))
-    dir[strlen (dir) - 1] = '\0';
-
-  separator = separator_null ? '\0' : '\n';
-
-  if (!*dir)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Invalid empty directory");
-      goto out;
-    }
-
-  if (metadata_text_path || metadata_bin_path)
-    {
-      metadata_mappedf = g_mapped_file_new (metadata_text_path ? metadata_text_path : metadata_bin_path, FALSE, error);
-      if (!metadata_mappedf)
-        goto out;
-      if (metadata_text_path)
-        {
-          metadata = g_variant_parse (G_VARIANT_TYPE ("a{sv}"),
-                                      g_mapped_file_get_contents (metadata_mappedf),
-                                      g_mapped_file_get_contents (metadata_mappedf) + g_mapped_file_get_length (metadata_mappedf),
-                                      NULL, error);
-          if (!metadata)
-            goto out;
-        }
-      else if (metadata_bin_path)
-        {
-          metadata_f = ot_util_new_file_for_path (metadata_bin_path);
-          if (!ot_util_variant_map (metadata_f, G_VARIANT_TYPE ("a{sv}"), &metadata, error))
-            goto out;
-        }
-      else
-        g_assert_not_reached ();
-    }
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (!branch)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "A branch must be specified with --branch");
-      goto out;
-    }
-
-  if (!subject)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "A subject must be specified with --subject");
-      goto out;
-    }
-
-  if (!(from_file || from_fd >= 0 || from_stdin))
-    {
-      /* We're using the current directory */
-
-    }
-
-  if (from_stdin)
-    from_fd = 0;
-  else if (from_file)
-    {
-      temp_fd = ot_util_open_file_read (from_file, error);
-      if (temp_fd < 0)
-        {
-          g_prefix_error (error, "Failed to open '%s': ", from_file);
-          goto out;
-        }
-      from_fd = temp_fd;
-    }
-  else
-    {
-      if (pipe (pipefd) < 0)
-        {
-          ot_util_set_error_from_errno (error, errno);
-          goto out;
-        }
-
-      out = (GOutputStream*)g_unix_output_stream_new (pipefd[1], TRUE);
-      from_fd = pipefd[0];
-
-      fdata.dir = ot_util_new_file_for_path (dir);
-      fdata.separator = separator;
-      fdata.out = out;
-      fdata.cancellable = NULL;
-
-      if (g_thread_create_full (find_thread, &fdata, 0, FALSE, FALSE, G_THREAD_PRIORITY_NORMAL, error) == NULL)
-        goto out;
-
-      out = NULL;
-    }
-
-  if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, metadata,
-                                            dir, from_fd, separator,
-                                            &commit_checksum, error))
-    goto out;
-
-  ret = TRUE;
-  g_print ("%s\n", g_checksum_get_string (commit_checksum));
- out:
-  g_clear_object (&out);
-  if (temp_fd >= 0)
-    (void)close (temp_fd);
-  if (pipefd[0] > 0)
-    (void) close (pipefd[0]);
-  if (pipefd[1] > 0)
-    (void) close (pipefd[1]);
-  g_free (dir);
-  if (metadata_mappedf)
-    g_mapped_file_unref (metadata_mappedf);
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  if (commit_checksum)
-    g_checksum_free (commit_checksum);
-  return ret;
-}
diff --git a/ostree/ot-builtin-compose.c b/ostree/ot-builtin-compose.c
deleted file mode 100644 (file)
index 31cfacc..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static char *compose_metadata_path;
-
-static GOptionEntry options[] = {
-  { "out-metadata", 0, 0, G_OPTION_ARG_FILENAME, &compose_metadata_path, "Output a file containing serialized metadata about the compose, in host endianness", "path" },
-  { NULL }
-};
-
-static void
-rm_rf (GFile *path)
-{
-  GFileInfo *finfo = NULL;
-  GFileEnumerator *path_enum = NULL;
-  guint32 type;
-  
-  finfo = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
-                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                             NULL, NULL);
-  if (!finfo)
-    goto out;
-
-  type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
-  if (type == G_FILE_TYPE_DIRECTORY)
-    {
-      path_enum = g_file_enumerate_children (path, OSTREE_GIO_FAST_QUERYINFO, 
-                                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                             NULL, NULL);
-      if (!path_enum)
-        goto out;
-
-      
-      g_clear_object (&finfo);
-      while ((finfo = g_file_enumerator_next_file (path_enum, NULL, NULL)) != NULL)
-        {
-          GFile *child = g_file_get_child (path, g_file_info_get_attribute_byte_string (finfo, "standard::name"));
-          rm_rf (child);
-          g_clear_object (&child);
-          g_clear_object (&finfo);
-        }
-    }
-
-  (void) g_file_delete (path, NULL, NULL);
-
- out:
-  g_clear_object (&finfo);
-  g_clear_object (&path_enum);
-}
-
-static gboolean
-merge_dir (GFile    *destination,
-           GFile    *src,
-           GError  **error)
-{
-  gboolean ret = FALSE;
-  char *dest_path = NULL;
-  char *src_path = NULL;
-  GError *temp_error = NULL;
-  GFileInfo *src_fileinfo = NULL;
-  GFileInfo *dest_fileinfo = NULL;
-  GFileEnumerator *src_enum = NULL;
-  GFile *dest_subfile = NULL;
-  GFile *src_subfile = NULL;
-  const char *name;
-  guint32 type;
-
-  dest_path = g_file_get_path (destination);
-  src_path = g_file_get_path (src);
-
-  dest_fileinfo = g_file_query_info (destination, OSTREE_GIO_FAST_QUERYINFO,
-                                     G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                     NULL, &temp_error);
-  if (dest_fileinfo)
-    {
-      type = g_file_info_get_attribute_uint32 (dest_fileinfo, "standard::type");
-      if (type != G_FILE_TYPE_DIRECTORY)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Attempting to replace non-directory %s with directory %s",
-                       dest_path, src_path);
-          goto out;
-        }
-
-      src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO, 
-                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                            NULL, error);
-      if (!src_enum)
-        goto out;
-
-      while ((src_fileinfo = g_file_enumerator_next_file (src_enum, NULL, &temp_error)) != NULL)
-        {
-          type = g_file_info_get_attribute_uint32 (src_fileinfo, "standard::type");
-          name = g_file_info_get_attribute_byte_string (src_fileinfo, "standard::name");
-      
-          dest_subfile = g_file_get_child (destination, name);
-          src_subfile = g_file_get_child (src, name);
-
-          if (type == G_FILE_TYPE_DIRECTORY)
-            {
-              if (!merge_dir (dest_subfile, src_subfile, error))
-                goto out;
-            }
-          else
-            {
-              if (!g_file_delete (dest_subfile, NULL, &temp_error))
-                {
-                  if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-                    g_clear_error (&temp_error);
-                  else
-                    {
-                      g_propagate_error (error, temp_error);
-                      goto out;
-                    }
-                }
-              if (!g_file_move (src_subfile, dest_subfile, 0, NULL, NULL, NULL, error))
-                goto out;
-            }
-          
-          g_clear_object (&dest_subfile);
-          g_clear_object (&src_subfile);
-        }
-      if (temp_error)
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-    }
-  else if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
-    {
-      g_clear_error (&temp_error);
-      if (!g_file_move (src, destination, 0, NULL, NULL, NULL, error))
-        goto out;
-    }
-  else
-    goto out;
-
-  ret = TRUE;
- out:
-  g_free (dest_path);
-  g_free (src_path);
-  g_clear_object (&src_fileinfo);
-  g_clear_object (&dest_fileinfo);
-  g_clear_object (&src_enum);
-  g_clear_object (&dest_subfile);
-  g_clear_object (&src_subfile);
-  return ret;
-}
-
-static gboolean
-compose_branch_on_dir (OstreeRepo *repo,
-                       GFile *destination,
-                       const char *branch,
-                       GVariantBuilder *metadata_builder,
-                       GError **error)
-{
-  char *destpath = NULL;
-  char *branchpath = NULL;
-  GFile *branchf = NULL;
-  GFileEnumerator *enumerator = NULL;
-  gboolean ret = FALSE;
-  char *branchrev = NULL;
-
-  if (!ostree_repo_resolve_rev (repo, branch, &branchrev, error))
-    goto out;
-  
-  destpath = g_file_get_path (destination);
-  if (g_str_has_suffix (destpath, "/"))
-    destpath[strlen (destpath) - 1] = '\0';
-  branchpath = g_strconcat (destpath, "-tmp-checkout-", branchrev, NULL);
-  branchf = ot_util_new_file_for_path (branchpath);
-
-  g_print ("Checking out %s (commit %s)...\n", branch, branchrev);
-  if (!ostree_repo_checkout (repo, branchrev, branchpath, NULL, error))
-    goto out;
-  g_print ("...done\n");
-  g_print ("Merging over destination...\n");
-  if (!merge_dir (destination, branchf, error))
-    goto out;
-
-  if (metadata_builder)
-    g_variant_builder_add (metadata_builder, "(ss)", branch, branchrev);
-
-  ret = TRUE;
- out:
-  if (branchf)
-    rm_rf (branchf);
-  g_clear_object (&enumerator);
-  g_clear_object (&branchf);
-  g_free (branchrev);
-  g_free (destpath);
-  g_free (branchpath);
-  return ret;
-}
-
-gboolean
-ostree_builtin_compose (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  OstreeCheckout *checkout = NULL;
-  const char *destination;
-  GFile *destf = NULL;
-  gboolean compose_metadata_builder_initialized = FALSE;
-  GVariantBuilder compose_metadata_builder;
-  gboolean commit_metadata_builder_initialized = FALSE;
-  GVariantBuilder commit_metadata_builder;
-  GVariant *commit_metadata = NULL;
-  GFile *metadata_f = NULL;
-  int i;
-
-  context = g_option_context_new ("DESTINATION BRANCH1 BRANCH2 ... - Merge multiple commits into a single filesystem tree");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 3)
-    {
-      gchar *help = g_option_context_get_help (context, TRUE, NULL);
-      g_printerr ("%s\n", help);
-      g_free (help);
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                               "DESTINATION and at least one COMMIT must be specified");
-      goto out;
-    }
-
-  destination = argv[1];
-  destf = ot_util_new_file_for_path (destination);
-  
-  if (compose_metadata_path)
-    {
-      compose_metadata_builder_initialized = TRUE;
-      g_variant_builder_init (&compose_metadata_builder, G_VARIANT_TYPE ("a(ss)"));
-    }
-  
-  for (i = 2; i < argc; i++)
-    {
-      const char *branch = argv[i];
-      
-      if (!compose_branch_on_dir (repo, destf, branch, compose_metadata_builder_initialized ? &compose_metadata_builder : NULL, error))
-        goto out;
-    }
-
-  if (compose_metadata_path)
-    {
-      commit_metadata_builder_initialized = TRUE;
-      g_variant_builder_init (&commit_metadata_builder, G_VARIANT_TYPE ("a{sv}"));
-
-      g_variant_builder_add (&commit_metadata_builder, "{sv}",
-                             "ostree-compose", g_variant_builder_end (&compose_metadata_builder));
-      compose_metadata_builder_initialized = FALSE;
-
-      metadata_f = ot_util_new_file_for_path (compose_metadata_path);
-
-      commit_metadata = g_variant_builder_end (&commit_metadata_builder);
-      if (!ot_util_variant_save (metadata_f, commit_metadata, NULL, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  if (compose_metadata_builder_initialized)
-    g_variant_builder_clear (&compose_metadata_builder);
-  if (commit_metadata_builder_initialized)
-    g_variant_builder_clear (&commit_metadata_builder);
-  if (context)
-    g_option_context_free (context);
-  if (commit_metadata)
-    g_variant_unref (commit_metadata);
-  g_clear_object (&repo);
-  g_clear_object (&checkout);
-  g_clear_object (&destf);
-  g_clear_object (&metadata_f);
-  return ret;
-}
diff --git a/ostree/ot-builtin-diff.c b/ostree/ot-builtin-diff.c
deleted file mode 100644 (file)
index 15344bf..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
-  { NULL }
-};
-
-gboolean
-ostree_builtin_diff (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  const char *target;
-  const char *rev;
-  GFile *targetf = NULL;
-  GPtrArray *modified = NULL;
-  GPtrArray *removed = NULL;
-  GPtrArray *added = NULL;
-
-  context = g_option_context_new ("REV TARGETDIR - Compare directory TARGETDIR against revision REV");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 3)
-    {
-      gchar *help = g_option_context_get_help (context, TRUE, NULL);
-      g_printerr ("%s\n", help);
-      g_free (help);
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                               "REV and TARGETDIR must be specified");
-      goto out;
-    }
-
-  rev = argv[1];
-  target = argv[2];
-  targetf = ot_util_new_file_for_path (target);
-  
-  if (!ostree_repo_diff (repo, rev, targetf, &modified, &removed, &added, NULL, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  g_clear_object (&repo);
-  g_clear_object (&targetf);
-  if (modified)
-    g_ptr_array_free (modified, TRUE);
-  if (removed)
-    g_ptr_array_free (removed, TRUE);
-  if (added)
-    g_ptr_array_free (added, TRUE);
-  return ret;
-}
diff --git a/ostree/ot-builtin-fsck.c b/ostree/ot-builtin-fsck.c
deleted file mode 100644 (file)
index 3cc72b9..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean quiet;
-
-static GOptionEntry options[] = {
-  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
-  { NULL }
-};
-
-typedef struct {
-  guint n_objects;
-  gboolean had_error;
-} OtFsckData;
-
-static gboolean
-checksum_packed_file (OtFsckData   *data,
-                      const char   *path,
-                      GChecksum   **out_checksum,
-                      GError      **error)
-{
-  gboolean ret = FALSE;
-  GChecksum *ret_checksum = NULL;
-  GFile *file = NULL;
-  char *metadata_buf = NULL;
-  GVariant *metadata = NULL;
-  GVariant *xattrs = NULL;
-  GFileInputStream *in = NULL;
-  guint32 metadata_len;
-  guint32 version, uid, gid, mode;
-  guint64 content_len;
-  gsize bytes_read;
-  char buf[8192];
-
-  file = ot_util_new_file_for_path (path);
-
-  in = g_file_read (file, NULL, error);
-  if (!in)
-    goto out;
-      
-  if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
-    goto out;
-      
-  metadata_len = GUINT32_FROM_BE (metadata_len);
-      
-  metadata_buf = g_malloc (metadata_len);
-      
-  if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
-    goto out;
-
-  metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
-                                      metadata_buf, metadata_len, FALSE, NULL, NULL);
-      
-  g_variant_get (metadata, "(uuuu@a(ayay)t)",
-                 &version, &uid, &gid, &mode,
-                 &xattrs, &content_len);
-  uid = GUINT32_FROM_BE (uid);
-  gid = GUINT32_FROM_BE (gid);
-  mode = GUINT32_FROM_BE (mode);
-  content_len = GUINT64_FROM_BE (content_len);
-
-  ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
-
-  do
-    {
-      if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
-        goto out;
-      g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
-    }
-  while (bytes_read > 0);
-
-  ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
-  g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
-
-  ret = TRUE;
-  *out_checksum = ret_checksum;
-  ret_checksum = NULL;
- out:
-  if (ret_checksum)
-    g_checksum_free (ret_checksum);
-  g_free (metadata_buf);
-  g_clear_object (&file);
-  g_clear_object (&in);
-  if (metadata)
-   g_variant_unref (metadata);
-  if (xattrs)
-    g_variant_unref (xattrs);
-  return ret;
-}
-
-static void
-object_iter_callback (OstreeRepo  *repo,
-                      const char    *path,
-                      GFileInfo     *file_info,
-                      gpointer       user_data)
-{
-  OtFsckData *data = user_data;
-  struct stat stbuf;
-  GChecksum *checksum = NULL;
-  GError *error = NULL;
-  char *dirname = NULL;
-  char *checksum_prefix = NULL;
-  char *checksum_string = NULL;
-  char *filename_checksum = NULL;
-  gboolean packed = FALSE;
-  OstreeObjectType objtype;
-  char *dot;
-
-  /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
-     if (nlinks < 2 && !quiet)
-     g_printerr ("note: floating object: %s\n", path); */
-
-  if (g_str_has_suffix (path, ".meta"))
-    objtype = OSTREE_OBJECT_TYPE_META;
-  else if (g_str_has_suffix (path, ".file"))
-    objtype = OSTREE_OBJECT_TYPE_FILE;
-  else if (g_str_has_suffix (path, ".packfile"))
-    {
-      objtype = OSTREE_OBJECT_TYPE_FILE;
-     packed = TRUE;
-    }
-  else
-    g_assert_not_reached ();
-
-  if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
-    {
-      if (!checksum_packed_file (data, path, &checksum, &error))
-        goto out;
-    }
-  else
-    {
-      if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
-        goto out;
-    }
-
-  filename_checksum = g_strdup (g_file_info_get_name (file_info));
-  dot = strrchr (filename_checksum, '.');
-  g_assert (dot != NULL);
-  *dot = '\0';
-  
-  dirname = g_path_get_dirname (path);
-  checksum_prefix = g_path_get_basename (dirname);
-  checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
-  
-  if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
-    {
-      data->had_error = TRUE;
-      g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
-                  path, g_checksum_get_string (checksum));
-    }
-
-  data->n_objects++;
-
- out:
-  if (checksum != NULL)
-    g_checksum_free (checksum);
-  g_free (dirname);
-  g_free (checksum_prefix);
-  g_free (checksum_string);
-  g_free (filename_checksum);
-  if (error != NULL)
-    {
-      g_printerr ("%s\n", error->message);
-      g_clear_error (&error);
-    }
-}
-
-gboolean
-ostree_builtin_fsck (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  OtFsckData data;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-
-  context = g_option_context_new ("- Check the repository for consistency");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  data.n_objects = 0;
-  data.had_error = FALSE;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
-    goto out;
-
-  if (data.had_error)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Encountered filesystem consistency errors");
-      goto out;
-    }
-  if (!quiet)
-    g_printerr ("Total Objects: %u\n", data.n_objects);
-
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  return ret;
-}
diff --git a/ostree/ot-builtin-init.c b/ostree/ot-builtin-init.c
deleted file mode 100644 (file)
index c58d639..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean archive;
-
-static GOptionEntry options[] = {
-  { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
-  { NULL }
-};
-
-#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
-                                 "repo_version=0\n")
-
-
-gboolean
-ostree_builtin_init (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context = NULL;
-  gboolean ret = FALSE;
-  GFile *repodir = NULL;
-  GFile *child = NULL;
-  GFile *grandchild = NULL;
-  GString *config_data = NULL;
-
-  context = g_option_context_new ("- Initialize a new empty repository");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repodir = ot_util_new_file_for_path (repo_path);
-
-  child = g_file_get_child (repodir, "config");
-
-  config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
-  g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
-  if (!g_file_replace_contents (child,
-                                config_data->str,
-                                config_data->len,
-                                NULL, FALSE, 0, NULL,
-                                NULL, error))
-    goto out;
-  g_clear_object (&child);
-
-  child = g_file_get_child (repodir, "objects");
-  if (!g_file_make_directory (child, NULL, error))
-    goto out;
-  g_clear_object (&child);
-
-  child = g_file_get_child (repodir, "tmp");
-  if (!g_file_make_directory (child, NULL, error))
-    goto out;
-  g_clear_object (&child);
-
-  child = g_file_get_child (repodir, "refs");
-  if (!g_file_make_directory (child, NULL, error))
-    goto out;
-
-  grandchild = g_file_get_child (child, "heads");
-  if (!g_file_make_directory (grandchild, NULL, error))
-    goto out;
-  g_clear_object (&grandchild);
-
-  grandchild = g_file_get_child (child, "remotes");
-  if (!g_file_make_directory (grandchild, NULL, error))
-    goto out;
-  g_clear_object (&grandchild);
-
-  g_clear_object (&child);
-
-  child = g_file_get_child (repodir, "tags");
-  if (!g_file_make_directory (child, NULL, error))
-    goto out;
-  g_clear_object (&child);
-
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  if (config_data)
-    g_string_free (config_data, TRUE);
-  g_clear_object (&repodir);
-  g_clear_object (&child);
-  g_clear_object (&grandchild);
-  return ret;
-}
diff --git a/ostree/ot-builtin-log.c b/ostree/ot-builtin-log.c
deleted file mode 100644 (file)
index ff82fde..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
-  { NULL }
-};
-
-gboolean
-ostree_builtin_log (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  GOutputStream *pager = NULL;
-  const char *rev;
-  GVariant *commit = NULL;
-  char *resolved_rev = NULL;
-
-  context = g_option_context_new ("- Show revision log");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  if (argc < 2)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "A revision must be specified");
-      goto out;
-    }
-                   
-  rev = argv[1];
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (!ot_util_spawn_pager (&pager, error))
-    goto out;
-
-  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
-    goto out;
-
-  while (TRUE)
-    {
-      OstreeSerializedVariantType type;
-      char *formatted = NULL;
-      guint32 version;
-      const char *parent;
-      const char *subject;
-      const char *body;
-      guint64 timestamp;
-      const char *contents;
-      const char *root_metadata;
-      GDateTime *time_obj = NULL;
-      char *formatted_date = NULL;
-      const char *body_newline;
-      gsize bytes_written;
-      GVariant *commit_metadata = NULL;
-      char *formatted_metadata = NULL;
-      
-      if (commit)
-        g_variant_unref (commit);
-      if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
-        goto out;
-
-      /* Ignore commit metadata for now */
-      g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
-                     &version, &commit_metadata, &parent, &subject, &body,
-                     &timestamp, &contents, &root_metadata);
-      version = GUINT32_FROM_BE (version);
-      timestamp = GUINT64_FROM_BE (timestamp);
-      time_obj = g_date_time_new_from_unix_utc (timestamp);
-      formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
-      g_date_time_unref (time_obj);
-      time_obj = NULL;
-
-      formatted_metadata = g_variant_print (commit_metadata, TRUE);
-      g_variant_unref (commit_metadata);
-      formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
-                                   resolved_rev, subject, formatted_date, formatted_metadata);
-      g_free (formatted_metadata);
-      g_free (formatted_date);
-      formatted_date = NULL;
-      
-      if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
-        {
-          g_free (formatted);
-          goto out;
-        }
-      g_free (formatted);
-      
-      body_newline = strchr (body, '\n');
-      do {
-        gsize len;
-        if (!g_output_stream_write_all (pager, "    ", 4, &bytes_written, NULL, error))
-          goto out;
-        len = body_newline ? body_newline - body : strlen (body);
-        if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
-          goto out;
-        if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
-          goto out;
-        body_newline = strchr (body, '\n');
-        if (!body_newline)
-          break;
-        else
-          body_newline += 1;
-      } while (*body_newline);
-
-      if (strcmp (parent, "") == 0)
-        break;
-      g_free (resolved_rev);
-      resolved_rev = g_strdup (parent);
-    }
-
-  if (!g_output_stream_close (pager, NULL, error))
-    goto out;
-  ret = TRUE;
- out:
-  g_free (resolved_rev);
-  if (context)
-    g_option_context_free (context);
-  if (commit)
-    g_variant_unref (commit);
-  g_clear_object (&repo);
-  return ret;
-}
diff --git a/ostree/ot-builtin-pull.c b/ostree/ot-builtin-pull.c
deleted file mode 100644 (file)
index d702219..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-#include <libsoup/soup-gnome.h>
-
-static GOptionEntry options[] = {
-  { NULL }
-};
-
-static gboolean
-fetch_uri (OstreeRepo  *repo,
-           SoupSession *soup,
-           SoupURI     *uri,
-           char       **temp_filename,
-           GError     **error)
-{
-  gboolean ret = FALSE;
-  SoupMessage *msg = NULL;
-  guint response;
-  char *template = NULL;
-  int fd;
-  SoupBuffer *buf = NULL;
-  GFile *tempf = NULL;
-  
-  msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
-  
-  response = soup_session_send_message (soup, msg);
-  if (response != 200)
-    {
-      char *uri_string = soup_uri_to_string (uri, FALSE);
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Failed to retrieve '%s': %d %s",
-                   uri_string, response, msg->reason_phrase);
-      g_free (uri_string);
-      goto out;
-    }
-
-  template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
-  
-  fd = g_mkstemp (template);
-  if (fd < 0)
-    {
-      ot_util_set_error_from_errno (error, errno);
-      goto out;
-    }
-  close (fd);
-  tempf = ot_util_new_file_for_path (template);
-
-  buf = soup_message_body_flatten (msg->response_body);
-
-  if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
-    goto out;
-  
-  *temp_filename = template;
-  template = NULL;
-
-  ret = TRUE;
- out:
-  g_free (template);
-  g_clear_object (&msg);
-  g_clear_object (&tempf);
-  return ret;
-}
-
-static gboolean
-store_object (OstreeRepo  *repo,
-              SoupSession *soup,
-              SoupURI     *baseuri,
-              const char  *object,
-              OstreeObjectType objtype,
-              gboolean    *did_exist,
-              GError     **error)
-{
-  gboolean ret = FALSE;
-  char *filename = NULL;
-  char *objpath = NULL;
-  char *relpath = NULL;
-  SoupURI *obj_uri = NULL;
-
-  objpath = ostree_get_relative_object_path (object, objtype, TRUE);
-  obj_uri = soup_uri_copy (baseuri);
-  relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
-  soup_uri_set_path (obj_uri, relpath);
-
-  if (!fetch_uri (repo, soup, obj_uri, &filename, error))
-    goto out;
-
-  if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  if (obj_uri)
-    soup_uri_free (obj_uri);
-  if (filename)
-    (void) unlink (filename);
-  g_free (filename);
-  g_free (objpath);
-  g_free (relpath);
-  return ret;
-}
-
-static gboolean
-store_tree_recurse (OstreeRepo   *repo,
-                    SoupSession  *soup,
-                    SoupURI      *base_uri,
-                    const char   *rev,
-                    GError      **error)
-{
-  gboolean ret = FALSE;
-  GVariant *tree = NULL;
-  GVariant *files_variant = NULL;
-  GVariant *dirs_variant = NULL;
-  OstreeSerializedVariantType metatype;
-  gboolean did_exist;
-  int i, n;
-
-  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-    goto out;
-
-  if (!did_exist)
-    {
-      if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
-        goto out;
-      
-      if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Tree metadata '%s' has wrong type %d, expected %d",
-                       rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
-          goto out;
-        }
-      
-      /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
-      g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
-      g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
-      
-      n = g_variant_n_children (files_variant);
-      for (i = 0; i < n; i++)
-        {
-          const char *filename;
-          const char *checksum;
-
-          g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
-
-          if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
-            goto out;
-        }
-      
-      n = g_variant_n_children (dirs_variant);
-      for (i = 0; i < n; i++)
-        {
-          const char *dirname;
-          const char *tree_checksum;
-          const char *meta_checksum;
-
-          g_variant_get_child (dirs_variant, i, "(sss)",
-                               &dirname, &tree_checksum, &meta_checksum);
-
-          if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
-            goto out;
-
-          if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-            goto out;
-        }
-    }
-
-  ret = TRUE;
- out:
-  if (tree)
-    g_variant_unref (tree);
-  if (files_variant)
-    g_variant_unref (files_variant);
-  if (dirs_variant)
-    g_variant_unref (dirs_variant);
-  return ret;
-}
-
-static gboolean
-store_commit_recurse (OstreeRepo   *repo,
-                      SoupSession  *soup,
-                      SoupURI      *base_uri,
-                      const char   *rev,
-                      GError      **error)
-{
-  gboolean ret = FALSE;
-  GVariant *commit = NULL;
-  OstreeSerializedVariantType metatype;
-  const char *tree_contents_checksum;
-  const char *tree_meta_checksum;
-  gboolean did_exist;
-
-  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-    goto out;
-
-  if (!did_exist)
-    {
-      if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
-        goto out;
-      
-      if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Commit '%s' has wrong type %d, expected %d",
-                       rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
-          goto out;
-        }
-      
-      /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
-      g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
-      g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
-      
-      if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
-        goto out;
-      
-      if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  if (commit)
-    g_variant_unref (commit);
-  return ret;
-}
-                      
-gboolean
-ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  const char *remote;
-  const char *branch;
-  char *remote_branch_ref_path = NULL;
-  char *key = NULL;
-  char *baseurl = NULL;
-  char *refpath = NULL;
-  char *temppath = NULL;
-  GKeyFile *config = NULL;
-  SoupURI *base_uri = NULL;
-  SoupURI *target_uri = NULL;
-  SoupSession *soup = NULL;
-  char *rev = NULL;
-
-  context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 3)
-    {
-      ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
-      goto out;
-    }
-
-  remote = argv[1];
-  branch = argv[2];
-
-  config = ostree_repo_get_config (repo);
-
-  key = g_strdup_printf ("remote \"%s\"", remote);
-  baseurl = g_key_file_get_string (config, key, "url", error);
-  if (!baseurl)
-    goto out;
-  base_uri = soup_uri_new (baseurl);
-  if (!base_uri)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Failed to parse url '%s'", baseurl);
-      goto out;
-    }
-  target_uri = soup_uri_copy (base_uri);
-  g_free (refpath);
-  refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
-  soup_uri_set_path (target_uri, refpath);
-  
-  soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
-                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
-                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
-                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
-                                             NULL);
-  if (!fetch_uri (repo, soup, target_uri, &temppath, error))
-    goto out;
-
-  rev = ot_util_get_file_contents_utf8 (temppath, error);
-  if (!rev)
-    goto out;
-  g_strchomp (rev);
-
-  if (!ostree_validate_checksum_string (rev, error))
-    goto out;
-
-  if (!store_commit_recurse (repo, soup, base_uri, rev, error))
-    goto out;
-
-  if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
-    goto out;
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  if (temppath)
-    (void) unlink (temppath);
-  g_free (temppath);
-  g_free (key);
-  g_free (rev);
-  g_free (baseurl);
-  g_free (refpath);
-  g_free (remote_branch_ref_path);
-  g_clear_object (&soup);
-  if (base_uri)
-    soup_uri_free (base_uri);
-  if (target_uri)
-    soup_uri_free (target_uri);
-  g_clear_object (&repo);
-  g_clear_object (&soup);
-  return ret;
-}
diff --git a/ostree/ot-builtin-remote.c b/ostree/ot-builtin-remote.c
deleted file mode 100644 (file)
index f9d4a2c..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
-  { NULL }
-};
-
-static void
-usage_error (GOptionContext *context, const char *message, GError **error)
-{
-  gchar *help = g_option_context_get_help (context, TRUE, NULL);
-  g_printerr ("%s\n", help);
-  g_free (help);
-  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       message);
-}
-
-gboolean
-ostree_builtin_remote (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  OstreeCheckout *checkout = NULL;
-  const char *op;
-  GKeyFile *config = NULL;
-
-  context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 2)
-    {
-      usage_error (context, "OPERATION must be specified", error);
-      goto out;
-    }
-
-  op = argv[1];
-
-  config = ostree_repo_copy_config (repo);
-
-  if (!strcmp (op, "add"))
-    {
-      char *key;
-      if (argc < 4)
-        {
-          usage_error (context, "NAME and URL must be specified", error);
-          goto out;
-        }
-      key = g_strdup_printf ("remote \"%s\"", argv[2]);
-      g_key_file_set_string (config, key, "url", argv[3]);
-      g_free (key);
-    }
-  else
-    {
-      usage_error (context, "Unknown operation", error);
-      goto out;
-    }
-
-  if (!ostree_repo_write_config (repo, config, error))
-    goto out;
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  if (config)
-    g_key_file_free (config);
-  g_clear_object (&repo);
-  g_clear_object (&checkout);
-  return ret;
-}
diff --git a/ostree/ot-builtin-rev-parse.c b/ostree/ot-builtin-rev-parse.c
deleted file mode 100644 (file)
index 3a83cd7..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static GOptionEntry options[] = {
-  { NULL }
-};
-
-gboolean
-ostree_builtin_rev_parse (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  const char *rev = "master";
-  char *resolved_rev = NULL;
-  GVariant *variant = NULL;
-  char *formatted_variant = NULL;
-
-  context = g_option_context_new ("REV - Output the target of a rev");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 2)
-    {
-      ot_util_usage_error (context, "REV must be specified", error);
-      goto out;
-    }
-  rev = argv[1];
-
-  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
-    goto out;
-
-  g_print ("%s\n", resolved_rev);
-  ret = TRUE;
- out:
-  g_free (resolved_rev);
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  if (variant)
-    g_variant_unref (variant);
-  g_free (formatted_variant);
-  return ret;
-}
diff --git a/ostree/ot-builtin-run-triggers.c b/ostree/ot-builtin-run-triggers.c
deleted file mode 100644 (file)
index dd33211..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean quiet;
-
-static GOptionEntry options[] = {
-  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
-  { NULL }
-};
-
-gboolean
-ostree_builtin_run_triggers (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  OstreeCheckout *checkout = NULL;
-  const char *dir;
-
-  context = g_option_context_new ("DIR - Run trigger scripts for directory");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc < 1)
-    {
-      gchar *help = g_option_context_get_help (context, TRUE, NULL);
-      g_printerr ("%s\n", help);
-      g_free (help);
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "DIR must be specified");
-      goto out;
-    }
-
-  dir = argv[1];
-
-  checkout = ostree_checkout_new (repo, dir);
-  if (!ostree_checkout_run_triggers (checkout, error))
-    goto out;
-  ret = TRUE;
- out:
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  g_clear_object (&checkout);
-  return ret;
-}
diff --git a/ostree/ot-builtin-show.c b/ostree/ot-builtin-show.c
deleted file mode 100644 (file)
index 3a2e9c3..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-builtins.h"
-#include "ostree.h"
-
-#include <glib/gi18n.h>
-
-static gboolean print_compose;
-static char* print_variant_type;
-
-static GOptionEntry options[] = {
-  { "print-compose", 0, 0, G_OPTION_ARG_NONE, &print_compose, "If given, show the branches which make up the given compose commit", NULL },
-  { "print-variant-type", 0, 0, G_OPTION_ARG_STRING, &print_variant_type, "If given, argument should be a filename and it will be interpreted as this type", NULL },
-  { NULL }
-};
-
-static void
-print_variant (GVariant *variant)
-{
-  char *formatted_variant = NULL;
-
-  formatted_variant = g_variant_print (variant, TRUE);
-  g_print ("%s\n", formatted_variant);
-
-  g_free (formatted_variant);
-}
-
-static gboolean
-do_print_variant_generic (const GVariantType *type,
-                          const char *filename,
-                          GError **error)
-{
-  gboolean ret = FALSE;
-  GFile *f = NULL;
-  GVariant *variant = NULL;
-
-  f = ot_util_new_file_for_path (filename);
-
-  if (!ot_util_variant_map (f, type, &variant, error))
-    goto out;
-
-  print_variant (variant);
-
-  ret = TRUE;
- out:
-  if (variant)
-    g_variant_unref (variant);
-  g_clear_object (&f);
-  return ret;
-}
-
-static gboolean
-show_repo_meta (OstreeRepo  *repo,
-                const char *rev,
-                const char *resolved_rev,
-                GError **error)
-{
-  OstreeSerializedVariantType type;
-  gboolean ret = FALSE;
-  GVariant *variant = NULL;
-
-  if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
-    goto out;
-  g_print ("Object: %s\nType: %d\n", resolved_rev, type);
-  print_variant (variant);
-
-  ret = TRUE;
- out:
-  if (variant)
-    g_variant_unref (variant);
-  return ret;
-}
-
-static gboolean
-do_print_compose (OstreeRepo  *repo,
-                  const char *rev,
-                  const char *resolved_rev,
-                  GError **error)
-{
-  gboolean ret = FALSE;
-  GVariant *variant = NULL;
-  GVariant *metadata = NULL;
-  GVariant *compose_contents = NULL;
-  GVariantIter *viter = NULL;
-  GHashTable *metadata_hash = NULL;
-  const char *branch;
-  const char *branchrev;
-
-  if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
-                                         resolved_rev, &variant, error))
-    goto out;
-      
-  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
-  metadata = g_variant_get_child_value (variant, 1);
-  metadata_hash = ot_util_variant_asv_to_hash_table (metadata);
-  
-  compose_contents = g_hash_table_lookup (metadata_hash, "ostree-compose");
-  if (!compose_contents)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Commit %s does not have compose metadata key \"ostree-compose\"", resolved_rev);
-      goto out;
-    }
-
-  g_variant_get_child (compose_contents, 0, "a(ss)", &viter);
-  while (g_variant_iter_next (viter, "(&s&s)", &branch, &branchrev))
-    {
-      g_print ("%s %s\n", branch, branchrev);
-    }
-
-  ret = TRUE;
- out:
-  if (variant)
-    g_variant_unref (variant);
-  if (viter)
-    g_variant_iter_free (viter);
-  if (metadata)
-    g_variant_unref (metadata);
-  if (metadata_hash)
-    g_hash_table_destroy (metadata_hash);
-  return ret;
-}
-
-gboolean
-ostree_builtin_show (int argc, char **argv, const char *repo_path, GError **error)
-{
-  GOptionContext *context;
-  gboolean ret = FALSE;
-  OstreeRepo *repo = NULL;
-  const char *rev = "master";
-  char *resolved_rev = NULL;
-
-  context = g_option_context_new ("- Output a metadata object");
-  g_option_context_add_main_entries (context, options, NULL);
-
-  if (!g_option_context_parse (context, &argc, &argv, error))
-    goto out;
-
-  repo = ostree_repo_new (repo_path);
-  if (!ostree_repo_check (repo, error))
-    goto out;
-
-  if (argc > 1)
-    rev = argv[1];
-
-  if (print_compose)
-    {
-      if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
-        goto out;
-
-      if (!do_print_compose (repo, rev, resolved_rev, error))
-        goto out;
-    }
-  else if (print_variant_type)
-    {
-      if (!do_print_variant_generic (G_VARIANT_TYPE (print_variant_type), rev, error))
-        goto out;
-    }
-  else
-    {
-      if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
-        goto out;
-
-      if (!show_repo_meta (repo, rev, resolved_rev, error))
-        goto out;
-    }
-  ret = TRUE;
- out:
-  g_free (resolved_rev);
-  if (context)
-    g_option_context_free (context);
-  g_clear_object (&repo);
-  return ret;
-}
diff --git a/ostree/ot-builtins.h b/ostree/ot-builtins.h
deleted file mode 100644 (file)
index 8337cc8..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2011 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#ifndef __OSTREE_BUILTINS__
-#define __OSTREE_BUILTINS__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-typedef enum {
-  OSTREE_BUILTIN_FLAG_NONE = 0,
-} OstreeBuiltinFlags;
-
-typedef struct {
-  const char *name;
-  gboolean (*fn) (int argc, char **argv, const char *repo, GError **error);
-  int flags; /* OstreeBuiltinFlags */
-} OstreeBuiltin;
-
-gboolean ostree_builtin_checkout (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_commit (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_compose (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_diff (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_init (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_log (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_pull (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_fsck (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_show (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *repo, GError **error);
-gboolean ostree_builtin_remote (int argc, char **argv, const char *repo, GError **error);
-
-G_END_DECLS
-
-#endif
diff --git a/src/libostree/ostree-checkout.c b/src/libostree/ostree-checkout.c
new file mode 100644 (file)
index 0000000..0d58498
--- /dev/null
@@ -0,0 +1,362 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+enum {
+  PROP_0,
+
+  PROP_REPO,
+  PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeCheckout, ostree_checkout, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_CHECKOUT, OstreeCheckoutPrivate))
+
+typedef struct _OstreeCheckoutPrivate OstreeCheckoutPrivate;
+
+struct _OstreeCheckoutPrivate {
+  OstreeRepo *repo;
+  char *path;
+};
+
+static void
+ostree_checkout_finalize (GObject *object)
+{
+  OstreeCheckout *self = OSTREE_CHECKOUT (object);
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+  g_free (priv->path);
+  g_clear_object (&priv->repo);
+
+  G_OBJECT_CLASS (ostree_checkout_parent_class)->finalize (object);
+}
+
+static void
+ostree_checkout_set_property(GObject         *object,
+                          guint            prop_id,
+                          const GValue    *value,
+                          GParamSpec      *pspec)
+{
+  OstreeCheckout *self = OSTREE_CHECKOUT (object);
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      priv->path = g_value_dup_string (value);
+      break;
+    case PROP_REPO:
+      priv->repo = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_checkout_get_property(GObject         *object,
+                          guint            prop_id,
+                          GValue          *value,
+                          GParamSpec      *pspec)
+{
+  OstreeCheckout *self = OSTREE_CHECKOUT (object);
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      g_value_set_string (value, priv->path);
+      break;
+    case PROP_REPO:
+      g_value_set_object (value, priv->repo);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static GObject *
+ostree_checkout_constructor (GType                  gtype,
+                           guint                  n_properties,
+                           GObjectConstructParam *properties)
+{
+  GObject *object;
+  GObjectClass *parent_class;
+  OstreeCheckoutPrivate *priv;
+
+  parent_class = G_OBJECT_CLASS (ostree_checkout_parent_class);
+  object = parent_class->constructor (gtype, n_properties, properties);
+
+  priv = GET_PRIVATE (object);
+
+  g_assert (priv->path != NULL);
+  
+  return object;
+}
+
+static void
+ostree_checkout_class_init (OstreeCheckoutClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (OstreeCheckoutPrivate));
+
+  object_class->constructor = ostree_checkout_constructor;
+  object_class->get_property = ostree_checkout_get_property;
+  object_class->set_property = ostree_checkout_set_property;
+  object_class->finalize = ostree_checkout_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_PATH,
+                                   g_param_spec_string ("path", "", "",
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (object_class,
+                                   PROP_REPO,
+                                   g_param_spec_object ("repo", "", "",
+                                                        OSTREE_TYPE_REPO,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_checkout_init (OstreeCheckout *self)
+{
+}
+
+OstreeCheckout*
+ostree_checkout_new (OstreeRepo  *repo,
+                     const char  *path)
+{
+  return g_object_new (OSTREE_TYPE_CHECKOUT, "repo", repo, "path", path, NULL);
+}
+
+static gboolean
+executable_exists_in_checkout (const char *path,
+                               const char *executable)
+{
+  int i;
+  const char *subdirs[] = {"bin", "sbin", "usr/bin", "usr/sbin"};
+
+  for (i = 0; i < G_N_ELEMENTS (subdirs); i++)
+    {
+      char *possible_path = g_build_filename (path, subdirs[i], executable, NULL);
+      gboolean exists;
+      
+      exists = g_file_test (possible_path, G_FILE_TEST_EXISTS);
+      g_free (possible_path);
+
+      if (exists)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+run_trigger (OstreeCheckout *self,
+             GFile          *trigger,
+             gboolean        requires_chroot,
+             GError        **error)
+{
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  char *path = NULL;
+  char *temp_path = NULL;
+  char *rel_temp_path = NULL;
+  GFile *temp_copy = NULL;
+  char *basename = NULL;
+  GPtrArray *args = NULL;
+  int estatus;
+
+  path = g_file_get_path (trigger);
+  basename = g_path_get_basename (path);
+
+  args = g_ptr_array_new ();
+  
+  if (requires_chroot)
+    {
+      temp_path = g_build_filename (priv->path, basename, NULL);
+      rel_temp_path = g_strconcat ("./", basename, NULL);
+      temp_copy = ot_util_new_file_for_path (temp_path);
+
+      if (!g_file_copy (trigger, temp_copy, 0, NULL, NULL, NULL, error))
+        goto out;
+
+      g_ptr_array_add (args, "chroot");
+      g_ptr_array_add (args, ".");
+      g_ptr_array_add (args, rel_temp_path);
+      g_ptr_array_add (args, NULL);
+    }
+  else
+    {
+      g_ptr_array_add (args, path);
+      g_ptr_array_add (args, NULL);
+    }
+      
+  g_print ("Running trigger: %s\n", path);
+  if (!g_spawn_sync (priv->path,
+                     (char**)args->pdata,
+                     NULL,
+                     G_SPAWN_SEARCH_PATH,
+                     NULL, NULL, NULL, NULL,
+                     &estatus,
+                     error))
+    {
+      g_prefix_error (error, "Failed to run trigger %s: ", basename);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (requires_chroot && temp_path)
+    (void)unlink (temp_path);
+    
+  g_free (path);
+  g_free (basename);
+  g_free (temp_path);
+  g_free (rel_temp_path);
+  g_clear_object (&temp_copy);
+  if (args)
+    g_ptr_array_free (args, TRUE);
+  return ret;
+}
+
+static gboolean
+check_trigger (OstreeCheckout *self,
+               GFile          *trigger,
+               GError        **error)
+{
+  OstreeCheckoutPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  GInputStream *instream = NULL;
+  GDataInputStream *datain = NULL;
+  GError *temp_error = NULL;
+  char *line;
+  gsize len;
+  gboolean requires_chroot = TRUE;
+  gboolean matches = FALSE;
+
+  instream = (GInputStream*)g_file_read (trigger, NULL, error);
+  if (!instream)
+    goto out;
+  datain = g_data_input_stream_new (instream);
+
+  while ((line = g_data_input_stream_read_line (datain, &len, NULL, &temp_error)) != NULL)
+    {
+      if (g_str_has_prefix (line, "# IfExecutable: "))
+        {
+          char *executable = g_strdup (line + strlen ("# IfExecutable: "));
+          g_strchomp (executable);
+          matches = executable_exists_in_checkout (priv->path, executable);
+          g_free (executable);
+        }
+
+      g_free (line);
+    }
+  if (line == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (matches)
+    {
+      if (!run_trigger (self, trigger, requires_chroot, error))
+        goto out;
+    }
+  
+  ret = TRUE;
+ out:
+  g_clear_object (&instream);
+  g_clear_object (&datain);
+  return ret;
+}
+
+gboolean
+ostree_checkout_run_triggers (OstreeCheckout *self,
+                              GError        **error)
+{
+  gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  char *triggerdir_path = NULL;
+  GFile *triggerdir = NULL;
+  GFileInfo *file_info = NULL;
+  GFileEnumerator *enumerator = NULL;
+
+  triggerdir_path = g_build_filename (LIBEXECDIR, "ostree", "triggers.d", NULL);
+  triggerdir = ot_util_new_file_for_path (triggerdir_path);
+
+  enumerator = g_file_enumerate_children (triggerdir, OSTREE_GIO_FAST_QUERYINFO, 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+      char *child_path = NULL;
+      GFile *child = NULL;
+      gboolean success;
+
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      if (type == G_FILE_TYPE_REGULAR && g_str_has_suffix (name, ".trigger"))
+        {
+          child_path = g_build_filename (triggerdir_path, name, NULL);
+          child = ot_util_new_file_for_path (child_path);
+
+          success = check_trigger (self, child, error);
+        }
+      else
+        success = TRUE;
+
+      g_object_unref (file_info);
+      g_free (child_path);
+      g_clear_object (&child);
+      if (!success)
+        goto out;
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  g_free (triggerdir_path);
+  g_clear_object (&triggerdir);
+  g_clear_object (&enumerator);
+  return ret;
+}
diff --git a/src/libostree/ostree-checkout.h b/src/libostree/ostree-checkout.h
new file mode 100644 (file)
index 0000000..042cdc8
--- /dev/null
@@ -0,0 +1,60 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CHECKOUT
+#define _OSTREE_CHECKOUT
+
+#include <ostree-repo.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_CHECKOUT ostree_checkout_get_type()
+#define OSTREE_CHECKOUT(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckout))
+#define OSTREE_CHECKOUT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+#define OSTREE_IS_CHECKOUT(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_IS_CHECKOUT_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_CHECKOUT))
+#define OSTREE_CHECKOUT_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_CHECKOUT, OstreeCheckoutClass))
+
+typedef struct {
+  GObject parent;
+} OstreeCheckout;
+
+typedef struct {
+  GObjectClass parent_class;
+} OstreeCheckoutClass;
+
+GType ostree_checkout_get_type (void);
+
+OstreeCheckout* ostree_checkout_new (OstreeRepo  *repo,
+                                     const char  *path);
+
+gboolean ostree_checkout_run_triggers (OstreeCheckout *checkout,
+                                       GError        **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_CHECKOUT */
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
new file mode 100644 (file)
index 0000000..ca0fb1f
--- /dev/null
@@ -0,0 +1,856 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+
+gboolean
+ostree_validate_checksum_string (const char *sha256,
+                                 GError    **error)
+{
+  if (strlen (sha256) != 64)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid rev '%s'", sha256);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+
+void
+ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode)
+{
+  guint32 perms = (mode & ~S_IFMT);
+  g_checksum_update (checksum, (guint8*) &uid, 4);
+  g_checksum_update (checksum, (guint8*) &gid, 4);
+  g_checksum_update (checksum, (guint8*) &perms, 4);
+}
+
+static char *
+canonicalize_xattrs (char *xattr_string, size_t len)
+{
+  char *p;
+  GSList *xattrs = NULL;
+  GSList *iter;
+  GString *result;
+
+  result = g_string_new (0);
+
+  p = xattr_string;
+  while (p < xattr_string+len)
+    {
+      xattrs = g_slist_prepend (xattrs, p);
+      p += strlen (p) + 1;
+    }
+
+  xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
+  for (iter = xattrs; iter; iter = iter->next)
+    g_string_append (result, iter->data);
+
+  g_slist_free (xattrs);
+  return g_string_free (result, FALSE);
+}
+
+static gboolean
+read_xattr_name_array (const char *path,
+                       const char *xattrs,
+                       size_t      len,
+                       GVariantBuilder *builder,
+                       GError  **error)
+{
+  gboolean ret = FALSE;
+  const char *p;
+
+  p = xattrs;
+  while (p < xattrs+len)
+    {
+      ssize_t bytes_read;
+      char *buf;
+
+      bytes_read = lgetxattr (path, p, NULL, 0);
+      if (bytes_read < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      if (bytes_read == 0)
+        continue;
+
+      buf = g_malloc (bytes_read);
+      if (lgetxattr (path, p, buf, bytes_read) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          g_free (buf);
+          goto out;
+        }
+      
+      g_variant_builder_add (builder, "(@ay@ay)",
+                             g_variant_new_bytestring (p),
+                             g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+                                                      buf, bytes_read, FALSE, g_free, buf));
+
+      p = p + strlen (p) + 1;
+    }
+  
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+GVariant *
+ostree_get_xattrs_for_path (const char *path,
+                              GError    **error)
+{
+  GVariant *ret = NULL;
+  GVariantBuilder builder;
+  char *xattr_names = NULL;
+  char *xattr_names_canonical = NULL;
+  ssize_t bytes_read;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
+
+  bytes_read = llistxattr (path, NULL, 0);
+
+  if (bytes_read < 0)
+    {
+      if (errno != ENOTSUP)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (bytes_read > 0)
+    {
+      xattr_names = g_malloc (bytes_read);
+      if (llistxattr (path, xattr_names, bytes_read) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
+      
+      if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
+        goto out;
+    }
+
+  ret = g_variant_builder_end (&builder);
+  g_variant_ref_sink (ret);
+ out:
+  if (!ret)
+    g_variant_builder_clear (&builder);
+  g_free (xattr_names);
+  g_free (xattr_names_canonical);
+  return ret;
+}
+
+gboolean
+ostree_stat_and_checksum_file (int dir_fd, const char *path,
+                               OstreeObjectType objtype,
+                               GChecksum **out_checksum,
+                               struct stat *out_stbuf,
+                               GError **error)
+{
+  GChecksum *content_sha256 = NULL;
+  GChecksum *content_and_meta_sha256 = NULL;
+  char *stat_string = NULL;
+  ssize_t bytes_read;
+  GVariant *xattrs = NULL;
+  int fd = -1;
+  DIR *temp_dir = NULL;
+  char *basename = NULL;
+  gboolean ret = FALSE;
+  char *symlink_target = NULL;
+  char *device_id = NULL;
+  struct stat stbuf;
+
+  basename = g_path_get_basename (path);
+
+  if (dir_fd == -1)
+    {
+      char *dirname = g_path_get_dirname (path);
+      temp_dir = opendir (dirname);
+      if (temp_dir == NULL)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          g_free (dirname);
+        }
+      g_free (dirname);
+      dir_fd = dirfd (temp_dir);
+    }
+
+  if (fstatat (dir_fd, basename, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  if (S_ISREG(stbuf.st_mode))
+    {
+      fd = ot_util_open_file_read_at (dir_fd, basename, error);
+      if (fd < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  if (objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      xattrs = ostree_get_xattrs_for_path (path, error);
+      if (!xattrs)
+        goto out;
+    }
+
+  content_sha256 = g_checksum_new (G_CHECKSUM_SHA256);
+  if (S_ISREG(stbuf.st_mode))
+    {
+      guint8 buf[8192];
+
+      while ((bytes_read = read (fd, buf, sizeof (buf))) > 0)
+        g_checksum_update (content_sha256, buf, bytes_read);
+      if (bytes_read < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (S_ISLNK(stbuf.st_mode))
+    {
+      symlink_target = g_malloc (PATH_MAX);
+
+      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+      
+      bytes_read = readlinkat (dir_fd, basename, symlink_target, PATH_MAX);
+      if (bytes_read < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      g_checksum_update (content_sha256, (guint8*)symlink_target, bytes_read);
+    }
+  else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
+    {
+      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+      device_id = g_strdup_printf ("%u", (guint)stbuf.st_rdev);
+      g_checksum_update (content_sha256, (guint8*)device_id, strlen (device_id));
+    }
+  else if (S_ISFIFO(stbuf.st_mode))
+    {
+      g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR,
+                   G_IO_ERROR_FAILED,
+                   "Unsupported file '%s' (must be regular, symbolic link, fifo, or character/block device)",
+                   path);
+      goto out;
+    }
+
+  content_and_meta_sha256 = g_checksum_copy (content_sha256);
+
+  if (objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      ostree_checksum_update_stat (content_and_meta_sha256, stbuf.st_uid,
+                                   stbuf.st_gid, stbuf.st_mode);
+      g_checksum_update (content_and_meta_sha256, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+    }
+
+  *out_stbuf = stbuf;
+  *out_checksum = content_and_meta_sha256;
+  ret = TRUE;
+ out:
+  if (fd >= 0)
+    close (fd);
+  if (temp_dir != NULL)
+    closedir (temp_dir);
+  g_free (symlink_target);
+  g_free (basename);
+  g_free (stat_string);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  if (content_sha256)
+    g_checksum_free (content_sha256);
+  return ret;
+}
+
+gboolean
+ostree_set_xattrs (const char *path, GVariant *xattrs, GCancellable *cancellable, GError **error)
+{
+  gboolean ret = FALSE;
+  int i, n;
+
+  n = g_variant_n_children (xattrs);
+  for (i = 0; i < n; i++)
+    {
+      const guint8* name;
+      GVariant *value;
+      const guint8* value_data;
+      gsize value_len;
+      gboolean loop_err;
+
+      g_variant_get_child (xattrs, i, "(^&ay@ay)",
+                           &name, &value);
+      value_data = g_variant_get_fixed_array (value, &value_len, 1);
+      
+      loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, XATTR_REPLACE) < 0;
+      
+      g_variant_unref (value);
+      if (loop_err)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+gboolean
+ostree_parse_metadata_file (const char                  *path,
+                            OstreeSerializedVariantType *out_type,
+                            GVariant                   **out_variant,
+                            GError                     **error)
+{
+  GFile *pathf = NULL;
+  gboolean ret = FALSE;
+  GVariant *ret_variant = NULL;
+  GVariant *container = NULL;
+  guint32 ret_type;
+
+  pathf = ot_util_new_file_for_path (path);
+  if (!ot_util_variant_map (pathf, G_VARIANT_TYPE (OSTREE_SERIALIZED_VARIANT_FORMAT),
+                            &container, error))
+    goto out;
+
+  g_variant_get (container, "(uv)",
+                 &ret_type, &ret_variant);
+  ret_type = GUINT32_FROM_BE (ret_type);
+  if (ret_type <= 0 || ret_type > OSTREE_SERIALIZED_VARIANT_LAST)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Corrupted metadata object '%s'; invalid type %d", path, ret_type);
+      goto out;
+    }
+
+  ret = TRUE;
+  *out_type = ret_type;
+  *out_variant = ot_util_variant_take_ref (ret_variant);
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  if (container != NULL)
+    g_variant_unref (container);
+  g_clear_object (&pathf);
+  return ret;
+}
+
+char *
+ostree_get_relative_object_path (const char *checksum,
+                                 OstreeObjectType type,
+                                 gboolean         archive)
+{
+  GString *path;
+  const char *type_string;
+
+  g_assert (strlen (checksum) == 64);
+
+  path = g_string_new ("objects/");
+
+  g_string_append_len (path, checksum, 2);
+  g_string_append_c (path, '/');
+  g_string_append (path, checksum + 2);
+  switch (type)
+    {
+    case OSTREE_OBJECT_TYPE_FILE:
+      if (archive)
+        type_string = ".packfile";
+      else
+        type_string = ".file";
+      break;
+    case OSTREE_OBJECT_TYPE_META:
+      type_string = ".meta";
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+  g_string_append (path, type_string);
+  return g_string_free (path, FALSE);
+}
+
+gboolean
+ostree_pack_object (GOutputStream     *output,
+                    GFile             *file,
+                    OstreeObjectType  objtype,
+                    GCancellable     *cancellable,
+                    GError          **error)
+{
+  gboolean ret = FALSE;
+  char *path = NULL;
+  GFileInfo *finfo = NULL;
+  GFileInputStream *instream = NULL;
+  gboolean pack_builder_initialized = FALSE;
+  GVariantBuilder pack_builder;
+  GVariant *pack_variant = NULL;
+  GVariant *xattrs = NULL;
+  gsize bytes_written;
+
+  path = g_file_get_path (file);
+
+  finfo = g_file_query_info (file, "standard::type,standard::size,standard::is-symlink,standard::symlink-target,unix::*",
+                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
+  if (!finfo)
+    goto out;
+
+  if (objtype == OSTREE_OBJECT_TYPE_META)
+    {
+      guint64 object_size_be = GUINT64_TO_BE ((guint64)g_file_info_get_size (finfo));
+      if (!g_output_stream_write_all (output, &object_size_be, 8, &bytes_written, cancellable, error))
+        goto out;
+
+      instream = g_file_read (file, NULL, error);
+      if (!instream)
+        goto out;
+      
+      if (g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error) < 0)
+        goto out;
+    }
+  else
+    {
+      guint32 uid, gid, mode;
+      guint32 device = 0;
+      guint32 metadata_size_be;
+      const char *target = NULL;
+      guint64 object_size;
+
+      uid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_UID);
+      gid = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_GID);
+      mode = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_MODE);
+
+      g_variant_builder_init (&pack_builder, G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT));
+      pack_builder_initialized = TRUE;
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (0));
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (uid));
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (gid));
+      g_variant_builder_add (&pack_builder, "u", GUINT32_TO_BE (mode));
+
+      xattrs = ostree_get_xattrs_for_path (path, error);
+      if (!xattrs)
+        goto out;
+      g_variant_builder_add (&pack_builder, "@a(ayay)", xattrs);
+
+      if (S_ISREG (mode))
+        {
+          object_size = (guint64)g_file_info_get_size (finfo);
+        }
+      else if (S_ISLNK (mode))
+        {
+          target = g_file_info_get_attribute_byte_string (finfo, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
+          object_size = strlen (target);
+        }
+      else if (S_ISBLK (mode) || S_ISCHR (mode))
+        {
+          device = g_file_info_get_attribute_uint32 (finfo, G_FILE_ATTRIBUTE_UNIX_DEVICE);
+          object_size = 4;
+        }
+      else if (S_ISFIFO (mode))
+        {
+          object_size = 0;
+        }
+      else
+        g_assert_not_reached ();
+
+      g_variant_builder_add (&pack_builder, "t", GUINT64_TO_BE (object_size));
+      pack_variant = g_variant_builder_end (&pack_builder);
+      pack_builder_initialized = FALSE;
+
+      metadata_size_be = GUINT32_TO_BE (g_variant_get_size (pack_variant));
+
+      if (!g_output_stream_write_all (output, &metadata_size_be, 4,
+                                      &bytes_written, cancellable, error))
+        goto out;
+      g_assert (bytes_written == 4);
+
+      if (!g_output_stream_write_all (output, g_variant_get_data (pack_variant), g_variant_get_size (pack_variant),
+                                      &bytes_written, cancellable, error))
+        goto out;
+
+      if (S_ISREG (mode))
+        {
+          instream = g_file_read (file, NULL, error);
+          if (!instream)
+            goto out;
+          bytes_written = g_output_stream_splice (output, (GInputStream*)instream, 0, cancellable, error);
+          if (bytes_written < 0)
+            goto out;
+          if (bytes_written != object_size)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "File size changed unexpectedly");
+              goto out;
+            }
+        }
+      else if (S_ISLNK (mode))
+        {
+          if (!g_output_stream_write_all (output, target, object_size,
+                                          &bytes_written, cancellable, error))
+            goto out;
+        }
+      else if (S_ISBLK (mode) || S_ISCHR (mode))
+        {
+          guint32 device_be = GUINT32_TO_BE (device);
+          g_assert (object_size == 4);
+          if (!g_output_stream_write_all (output, &device_be, object_size,
+                                          &bytes_written, cancellable, error))
+            goto out;
+          g_assert (bytes_written == 4);
+        }
+      else if (S_ISFIFO (mode))
+        {
+        }
+      else
+        g_assert_not_reached ();
+    }
+  
+  ret = TRUE;
+ out:
+  g_free (path);
+  g_clear_object (&finfo);
+  g_clear_object (&instream);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  if (pack_builder_initialized)
+    g_variant_builder_clear (&pack_builder);
+  if (pack_variant)
+    g_variant_unref (pack_variant);
+  return ret;
+}
+
+static gboolean
+splice_and_checksum (GOutputStream  *out,
+                     GInputStream   *in,
+                     GChecksum      *checksum,
+                     GCancellable   *cancellable,
+                     GError        **error)
+{
+  gboolean ret = FALSE;
+  
+  if (checksum != NULL)
+    {
+      gsize bytes_read, bytes_written;
+      char buf[4096];
+      do
+        {
+          if (!g_input_stream_read_all (in, buf, sizeof(buf), &bytes_read, cancellable, error))
+            goto out;
+          if (checksum)
+            g_checksum_update (checksum, (guint8*)buf, bytes_read);
+          if (!g_output_stream_write_all (out, buf, bytes_read, &bytes_written, cancellable, error))
+            goto out;
+        }
+      while (bytes_read > 0);
+    }
+  else
+    {
+      if (g_output_stream_splice (out, in, 0, cancellable, error) < 0)
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+unpack_meta (const char   *path,
+             const char   *dest_path,    
+             GChecksum   **out_checksum,
+             GError      **error)
+{
+  gboolean ret = FALSE;
+  GFile *file = NULL;
+  GFile *dest_file = NULL;
+  GFileInputStream *in = NULL;
+  GChecksum *ret_checksum = NULL;
+  GFileOutputStream *out = NULL;
+
+  file = ot_util_new_file_for_path (path);
+  dest_file = ot_util_new_file_for_path (dest_path);
+
+  if (out_checksum)
+    ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+  in = g_file_read (file, NULL, error);
+  if (!in)
+    goto out;
+
+  out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+  if (!out)
+    goto out;
+
+  if (!splice_and_checksum ((GOutputStream*)out, (GInputStream*)in, ret_checksum, NULL, error))
+    goto out;
+
+  if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+    goto out;
+
+  ret = TRUE;
+  if (out_checksum)
+    *out_checksum = ret_checksum;
+  ret_checksum = NULL;
+ out:
+  if (!ret)
+    (void) unlink (dest_path);
+  if (ret_checksum)
+    g_checksum_free (ret_checksum);
+  g_clear_object (&file);
+  g_clear_object (&dest_file);
+  g_clear_object (&in);
+  return ret;
+}
+
+gboolean
+ostree_parse_packed_file (GFile            *file,
+                          GVariant    **out_metadata,
+                          GInputStream **out_content,
+                          GCancellable *cancellable,
+                          GError      **error)
+{
+  gboolean ret = FALSE;
+  char *metadata_buf = NULL;
+  GVariant *ret_metadata = NULL;
+  GFileInputStream *in = NULL;
+  guint32 metadata_len;
+  gsize bytes_read;
+
+  in = g_file_read (file, NULL, error);
+  if (!in)
+    goto out;
+      
+  if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+    goto out;
+  if (bytes_read != 4)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile; too short while reading metadata length");
+      goto out;
+    }
+      
+  metadata_len = GUINT32_FROM_BE (metadata_len);
+  if (metadata_len > OSTREE_MAX_METADATA_SIZE)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile; metadata length %u is larger than maximum %u",
+                   metadata_len, OSTREE_MAX_METADATA_SIZE);
+      goto out;
+    }
+  metadata_buf = g_malloc (metadata_len);
+
+  if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+    goto out;
+  if (bytes_read != metadata_len)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile; too short while reading metadata");
+      goto out;
+    }
+
+  ret_metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+                                          metadata_buf, metadata_len, FALSE,
+                                          (GDestroyNotify)g_free,
+                                          metadata_buf);
+  metadata_buf = NULL;
+
+  ret = TRUE;
+  *out_metadata = ret_metadata;
+  ret_metadata = NULL;
+  *out_content = (GInputStream*)in;
+  in = NULL;
+ out:
+  g_clear_object (&in);
+  if (ret_metadata)
+   g_variant_unref (ret_metadata);
+  return ret;
+}
+
+static gboolean
+unpack_file (const char   *path,
+             const char   *dest_path,    
+             GChecksum   **out_checksum,
+             GError      **error)
+{
+  gboolean ret = FALSE;
+  GFile *file = NULL;
+  GFile *dest_file = NULL;
+  GVariant *metadata = NULL;
+  GVariant *xattrs = NULL;
+  GInputStream *in = NULL;
+  GFileOutputStream *out = NULL;
+  GChecksum *ret_checksum = NULL;
+  guint32 version, uid, gid, mode;
+  guint64 content_len;
+  gsize bytes_read;
+
+  file = ot_util_new_file_for_path (path);
+
+  if (!ostree_parse_packed_file (file, &metadata, &in, NULL, error))
+    goto out;
+
+  g_variant_get (metadata, "(uuuu@a(ayay)t)",
+                 &version, &uid, &gid, &mode,
+                 &xattrs, &content_len);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+  content_len = GUINT64_FROM_BE (content_len);
+
+  dest_file = ot_util_new_file_for_path (dest_path);
+      
+  if (out_checksum)
+    ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+  if (S_ISREG (mode))
+    {
+      out = g_file_replace (dest_file, NULL, FALSE, 0, NULL, error);
+      if (!out)
+        goto out;
+
+      if (!splice_and_checksum ((GOutputStream*)out, in, ret_checksum, NULL, error))
+        goto out;
+
+      if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+        goto out;
+    }
+  else if (S_ISLNK (mode))
+    {
+      char target[PATH_MAX+1];
+
+      if (!g_input_stream_read_all (in, target, sizeof(target)-1, &bytes_read, NULL, error))
+        goto out;
+      target[bytes_read] = '\0';
+      if (ret_checksum)
+        g_checksum_update (ret_checksum, (guint8*)target, bytes_read);
+      if (symlink (target, dest_path) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (S_ISCHR (mode) || S_ISBLK (mode))
+    {
+      guint32 dev;
+
+      if (!g_input_stream_read_all (in, &dev, 4, &bytes_read, NULL, error))
+        goto out;
+      if (bytes_read != 4)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Corrupted packfile; too short while reading device id");
+          goto out;
+        }
+      dev = GUINT32_FROM_BE (dev);
+      if (ret_checksum)
+        g_checksum_update (ret_checksum, (guint8*)&dev, 4);
+      if (mknod (dest_path, mode, dev) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else if (S_ISFIFO (mode))
+    {
+      if (mkfifo (dest_path, mode) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile; invalid mode %u", mode);
+      goto out;
+    }
+
+  if (!S_ISLNK (mode))
+    {
+      if (chmod (dest_path, mode) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+    }
+
+  if (!ostree_set_xattrs (dest_path, xattrs, NULL, error))
+    goto out;
+
+  if (ret_checksum)
+    {
+      ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+      g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+    }
+
+  ret = TRUE;
+  if (out_checksum)
+    *out_checksum = ret_checksum;
+  ret_checksum = NULL;
+ out:
+  if (!ret)
+    (void) unlink (dest_path);
+  if (ret_checksum)
+    g_checksum_free (ret_checksum);
+  g_clear_object (&file);
+  g_clear_object (&dest_file);
+  g_clear_object (&in);
+  g_clear_object (&out);
+  if (metadata)
+   g_variant_unref (metadata);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  return ret;
+}
+
+gboolean
+ostree_unpack_object (const char   *path,
+                      OstreeObjectType  objtype,
+                      const char   *dest_path,    
+                      GChecksum   **out_checksum,
+                      GError      **error)
+{
+  if (objtype == OSTREE_OBJECT_TYPE_META)
+    return unpack_meta (path, dest_path, out_checksum, error);
+  else
+    return unpack_file (path, dest_path, out_checksum, error);
+}
+  
+
+
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
new file mode 100644 (file)
index 0000000..d67f42f
--- /dev/null
@@ -0,0 +1,155 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_CORE
+#define _OSTREE_CORE
+
+#include <otutil.h>
+
+G_BEGIN_DECLS
+
+#define OSTREE_MAX_METADATA_SIZE (1 << 26)
+
+#define OSTREE_GIO_FAST_QUERYINFO "standard::name,standard::type,standard::is-symlink,standard::symlink-target,standard::is-hidden,unix::*"
+
+#define OSTREE_EMPTY_STRING_SHA256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
+typedef enum {
+  OSTREE_OBJECT_TYPE_FILE = 1,
+  OSTREE_OBJECT_TYPE_META = 2,
+} OstreeObjectType;
+
+typedef enum {
+  OSTREE_SERIALIZED_TREE_VARIANT = 1,
+  OSTREE_SERIALIZED_COMMIT_VARIANT = 2,
+  OSTREE_SERIALIZED_DIRMETA_VARIANT = 3,
+  OSTREE_SERIALIZED_XATTR_VARIANT = 4
+} OstreeSerializedVariantType;
+#define OSTREE_SERIALIZED_VARIANT_LAST 4
+
+#define OSTREE_SERIALIZED_VARIANT_FORMAT "(uv)"
+
+/*
+ * xattr objects:
+ * a(ayay) - array of (name, value) pairs, both binary data, though name is a bytestring
+ */
+#define OSTREE_XATTR_GVARIANT_FORMAT "a(ayay)"
+
+#define OSTREE_DIR_META_VERSION 0
+/*
+ * dirmeta objects:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ */
+#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))"
+
+#define OSTREE_TREE_VERSION 0
+/*
+ * Tree objects:
+ * u - Version
+ * a{sv} - Metadata
+ * a(ss) - array of (filename, checksum) for files
+ * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories
+ */
+#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)"
+
+#define OSTREE_COMMIT_VERSION 0
+/*
+ * Commit objects:
+ * u - Version
+ * a{sv} - Metadata
+ * s - parent checksum (empty string for initial)
+ * s - subject 
+ * s - body
+ * t - Timestamp in seconds since the epoch (UTC)
+ * s - Root tree contents
+ * s - Root tree metadata
+ */
+#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)"
+
+gboolean ostree_validate_checksum_string (const char *sha256,
+                                          GError    **error);
+
+char *ostree_get_relative_object_path (const char *checksum,
+                                       OstreeObjectType type,
+                                       gboolean         archive);
+
+GVariant *ostree_get_xattrs_for_path (const char   *path,
+                                      GError     **error);
+
+gboolean ostree_set_xattrs (const char *path, GVariant *xattrs,
+                            GCancellable *cancellable, GError **error);
+
+gboolean ostree_parse_metadata_file (const char                  *path,
+                                     OstreeSerializedVariantType *out_type,
+                                     GVariant                   **out_variant,
+                                     GError                     **error);
+
+gboolean ostree_stat_and_checksum_file (int dirfd, const char *path,
+                                        OstreeObjectType type,
+                                        GChecksum **out_checksum,
+                                        struct stat *out_stbuf,
+                                        GError **error);
+
+/* Packed files:
+ *
+ * guint32 metadata_length [metadata gvariant] [content]
+ *
+ * metadata variant:
+ * u - Version
+ * u - uid
+ * u - gid
+ * u - mode
+ * a(ayay) - xattrs
+ * t - content length
+ *
+ * And then following the end of the variant is the content.  If
+ * symlink, then this is the target; if device, then device ID as
+ * network byte order uint32.
+ */
+#define OSTREE_PACK_FILE_VARIANT_FORMAT "(uuuua(ayay)t)"
+
+gboolean  ostree_pack_object (GOutputStream     *output,
+                              GFile             *file,
+                              OstreeObjectType  objtype,
+                              GCancellable     *cancellable,
+                              GError          **error);
+
+gboolean ostree_parse_packed_file (GFile            *file,
+                                   GVariant    **out_metadata,
+                                   GInputStream **out_content,
+                                   GCancellable *cancellable,
+                                   GError      **error);
+
+gboolean ostree_unpack_object (const char   *path,
+                               OstreeObjectType  objtype,
+                               const char   *dest_path,    
+                               GChecksum   **out_checksum,
+                               GError      **error);
+
+void ostree_checksum_update_stat (GChecksum *checksum, guint32 uid, guint32 gid, guint32 mode);
+
+
+#endif /* _OSTREE_REPO */
diff --git a/src/libostree/ostree-repo-file-enumerator.c b/src/libostree/ostree-repo-file-enumerator.c
new file mode 100644 (file)
index 0000000..7858bde
--- /dev/null
@@ -0,0 +1,143 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+#include <string.h>
+
+struct _OstreeRepoFileEnumerator
+{
+  GFileEnumerator parent;
+
+  OstreeRepoFile *dir;
+  char *attributes;
+  GFileQueryInfoFlags flags;
+
+  int index;
+};
+
+#define ostree_repo_file_enumerator_get_type _ostree_repo_file_enumerator_get_type
+G_DEFINE_TYPE (OstreeRepoFileEnumerator, ostree_repo_file_enumerator, G_TYPE_FILE_ENUMERATOR);
+
+static GFileInfo *ostree_repo_file_enumerator_next_file (GFileEnumerator  *enumerator,
+                                                    GCancellable     *cancellable,
+                                                    GError          **error);
+static gboolean   ostree_repo_file_enumerator_close     (GFileEnumerator  *enumerator,
+                                                    GCancellable     *cancellable,
+                                                    GError          **error);
+
+
+static void
+ostree_repo_file_enumerator_dispose (GObject *object)
+{
+  OstreeRepoFileEnumerator *self;
+
+  self = OSTREE_REPO_FILE_ENUMERATOR (object);
+
+  g_clear_object (&self->dir);
+  g_free (self->attributes);
+  
+  if (G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose)
+    G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_enumerator_finalize (GObject *object)
+{
+  OstreeRepoFileEnumerator *self;
+
+  self = OSTREE_REPO_FILE_ENUMERATOR (object);
+  (void)self;
+
+  G_OBJECT_CLASS (ostree_repo_file_enumerator_parent_class)->finalize (object);
+}
+
+
+static void
+ostree_repo_file_enumerator_class_init (OstreeRepoFileEnumeratorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
+  
+  gobject_class->finalize = ostree_repo_file_enumerator_finalize;
+  gobject_class->dispose = ostree_repo_file_enumerator_dispose;
+
+  enumerator_class->next_file = ostree_repo_file_enumerator_next_file;
+  enumerator_class->close_fn = ostree_repo_file_enumerator_close;
+}
+
+static void
+ostree_repo_file_enumerator_init (OstreeRepoFileEnumerator *self)
+{
+}
+
+GFileEnumerator *
+_ostree_repo_file_enumerator_new (OstreeRepoFile       *dir,
+                                 const char           *attributes,
+                                 GFileQueryInfoFlags   flags,
+                                 GCancellable         *cancellable,
+                                 GError              **error)
+{
+  OstreeRepoFileEnumerator *self;
+  
+  self = g_object_new (OSTREE_TYPE_REPO_FILE_ENUMERATOR,
+                      "container", dir,
+                      NULL);
+
+  self->dir = g_object_ref (dir);
+  self->attributes = g_strdup (attributes);
+  self->flags = flags;
+  
+  return G_FILE_ENUMERATOR (self);
+}
+
+static GFileInfo *
+ostree_repo_file_enumerator_next_file (GFileEnumerator  *enumerator,
+                                      GCancellable     *cancellable,
+                                      GError          **error)
+{
+  OstreeRepoFileEnumerator *self = OSTREE_REPO_FILE_ENUMERATOR (enumerator);
+  gboolean ret = FALSE;
+  GFileInfo *info = NULL;
+
+  if (!_ostree_repo_file_tree_query_child (self->dir, self->index,
+                                           self->attributes, self->flags,
+                                           &info, cancellable, error))
+    goto out;
+
+  self->index++;
+
+  ret = TRUE;
+ out:
+  if (!ret)
+    g_clear_object (&info);
+  return info;
+}
+
+static gboolean
+ostree_repo_file_enumerator_close (GFileEnumerator  *enumerator,
+                                  GCancellable     *cancellable,
+                                  GError          **error)
+{
+  return TRUE;
+}
diff --git a/src/libostree/ostree-repo-file-enumerator.h b/src/libostree/ostree-repo-file-enumerator.h
new file mode 100644 (file)
index 0000000..499a01b
--- /dev/null
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_REPO_FILE_ENUMERATOR
+#define _OSTREE_REPO_FILE_ENUMERATOR
+
+#include "ostree-repo-file.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE_ENUMERATOR         (_ostree_repo_file_enumerator_get_type ())
+#define OSTREE_REPO_FILE_ENUMERATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumerator))
+#define OSTREE_REPO_FILE_ENUMERATOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_IS_REPO_FILE_ENUMERATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE_ENUMERATOR))
+#define OSTREE_REPO_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE_ENUMERATOR, OstreeRepoFileEnumeratorClass))
+
+typedef struct _OstreeRepoFileEnumerator        OstreeRepoFileEnumerator;
+typedef struct _OstreeRepoFileEnumeratorClass   OstreeRepoFileEnumeratorClass;
+
+struct _OstreeRepoFileEnumeratorClass
+{
+  GFileEnumeratorClass parent_class;
+};
+
+GType   _ostree_repo_file_enumerator_get_type (void) G_GNUC_CONST;
+
+GFileEnumerator * _ostree_repo_file_enumerator_new      (OstreeRepoFile       *dir,
+                                                        const char           *attributes,
+                                                        GFileQueryInfoFlags   flags,
+                                                        GCancellable         *cancellable,
+                                                        GError              **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libostree/ostree-repo-file.c b/src/libostree/ostree-repo-file.c
new file mode 100644 (file)
index 0000000..a3d5e79
--- /dev/null
@@ -0,0 +1,1428 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree-repo-file-enumerator.h"
+
+static void ostree_repo_file_file_iface_init (GFileIface *iface);
+
+static void
+tree_replace_contents (OstreeRepoFile  *self,
+                       GVariant        *new_files,
+                       GVariant        *new_dirs);
+
+struct _OstreeRepoFile
+{
+  GObject parent_instance;
+
+  OstreeRepo *repo;
+
+  char *commit;
+  GError *commit_resolve_error;
+  
+  OstreeRepoFile *parent;
+  int index;
+  char *name;
+
+  char *tree_contents_checksum;
+  GVariant *tree_contents;
+  char *tree_metadata_checksum;
+  GVariant *tree_metadata;
+};
+
+#define ostree_repo_file_get_type _ostree_repo_file_get_type
+G_DEFINE_TYPE_WITH_CODE (OstreeRepoFile, ostree_repo_file, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
+                                               ostree_repo_file_file_iface_init))
+
+static void
+ostree_repo_file_finalize (GObject *object)
+{
+  OstreeRepoFile *self;
+
+  self = OSTREE_REPO_FILE (object);
+
+  if (self->tree_contents)
+    g_variant_unref (self->tree_contents);
+  if (self->tree_metadata)
+    g_variant_unref (self->tree_metadata);
+  g_free (self->tree_contents_checksum);
+  g_free (self->tree_metadata_checksum);
+  g_free (self->commit);
+  g_free (self->name);
+
+  G_OBJECT_CLASS (ostree_repo_file_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_file_dispose (GObject *object)
+{
+  OstreeRepoFile *self;
+
+  self = OSTREE_REPO_FILE (object);
+
+  g_clear_object (&self->repo);
+  g_clear_object (&self->parent);
+
+  if (G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose)
+    G_OBJECT_CLASS (ostree_repo_file_parent_class)->dispose (object);
+}
+
+static void
+ostree_repo_file_class_init (OstreeRepoFileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = ostree_repo_file_finalize;
+  gobject_class->dispose = ostree_repo_file_dispose;
+}
+
+static void
+ostree_repo_file_init (OstreeRepoFile *self)
+{
+  self->index = -1;
+}
+
+static gboolean
+set_error_noent (GFile *self, GError **error)
+{
+  char *path = g_file_get_path (self);
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+               "No such file or directory: %s", path);
+  g_free (path);
+  return FALSE;
+}
+
+GFile * 
+_ostree_repo_file_new_root (OstreeRepo  *repo,
+                            const char  *commit)
+{
+  OstreeRepoFile *self;
+
+  g_return_val_if_fail (repo != NULL, NULL);
+  g_return_val_if_fail (commit != NULL, NULL);
+  g_return_val_if_fail (strlen (commit) == 64, NULL);
+
+  self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+  self->repo = g_object_ref (repo);
+  self->commit = g_strdup (commit);
+
+  return G_FILE (self);
+}
+
+
+GFile *
+_ostree_repo_file_new_child (OstreeRepoFile *parent,
+                             const char  *name)
+{
+  OstreeRepoFile *self;
+  
+  self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+  self->repo = g_object_ref (parent->repo);
+  self->parent = g_object_ref (parent);
+  self->name = g_strdup (name);
+
+  return G_FILE (self);
+}
+
+OstreeRepoFile *
+_ostree_repo_file_new_empty_tree (OstreeRepo  *repo)
+{
+  OstreeRepoFile *self;
+  
+  self = g_object_new (OSTREE_TYPE_REPO_FILE, NULL);
+  self->repo = g_object_ref (repo);
+
+  tree_replace_contents (self, NULL, NULL);
+
+  return self;
+}
+
+static gboolean
+do_resolve_commit (OstreeRepoFile  *self,
+                   GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *commit = NULL;
+  GVariant *root_contents = NULL;
+  GVariant *root_metadata = NULL;
+  const char *tree_contents_checksum;
+  const char *tree_meta_checksum;
+
+  g_assert (self->parent == NULL);
+
+  if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
+                                         self->commit, &commit, error))
+    goto out;
+
+  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+  g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+  g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+
+  if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+                                         tree_contents_checksum, &root_contents,
+                                        error))
+    goto out;
+
+  if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                         tree_meta_checksum, &root_metadata,
+                                        error))
+    goto out;
+  
+  self->tree_metadata = root_metadata;
+  root_metadata = NULL;
+  self->tree_contents = root_contents;
+  root_contents = NULL;
+
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  if (root_contents)
+    g_variant_unref (root_contents);
+  return ret;
+}
+
+static gboolean
+do_resolve_nonroot (OstreeRepoFile     *self,
+                    GError            **error)
+{
+  gboolean ret = FALSE;
+  GVariant *container = NULL;
+  GVariant *tree_contents = NULL;
+  GVariant *tree_metadata = NULL;
+  gboolean is_dir;
+  int i;
+
+  i = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, &container);
+  
+  if (i < 0)
+    {
+      set_error_noent ((GFile*)self, error);
+      goto out;
+    }
+
+  if (is_dir)
+    {
+      const char *name;
+      const char *content_checksum;
+      const char *metadata_checksum;
+      GVariant *files_variant;
+
+      files_variant = g_variant_get_child_value (self->parent->tree_contents, 2);
+      self->index = g_variant_n_children (files_variant) + i;
+      g_variant_unref (files_variant);
+
+      g_variant_get_child (container, i, "(&s&s&s)",
+                           &name, &content_checksum, &metadata_checksum);
+          
+      if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_TREE_VARIANT,
+                                             content_checksum, &tree_contents,
+                                             error))
+        goto out;
+          
+      if (!ostree_repo_load_variant_checked (self->repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                             metadata_checksum, &tree_metadata,
+                                             error))
+        goto out;
+
+      self->tree_contents = tree_contents;
+      tree_contents = NULL;
+      self->tree_metadata = tree_metadata;
+      tree_metadata = NULL;
+    }
+  else
+    self->index = i;
+
+  ret = TRUE;
+ out:
+  if (container)
+    g_variant_unref (container);
+  if (tree_metadata)
+    g_variant_unref (tree_metadata);
+  if (tree_contents)
+    g_variant_unref (tree_contents);
+  return ret;
+}
+
+gboolean
+_ostree_repo_file_ensure_resolved (OstreeRepoFile  *self,
+                                   GError         **error)
+{
+  if (self->commit_resolve_error != NULL)
+    goto out;
+
+  if (self->parent == NULL)
+    {
+      if (self->tree_contents == NULL)
+        (void)do_resolve_commit (self, &(self->commit_resolve_error));
+    }
+  else if (self->index == -1)
+    {
+      (void)do_resolve_nonroot (self, &(self->commit_resolve_error));
+    }
+  
+ out:
+  if (self->commit_resolve_error)
+    {
+      if (error)
+       *error = g_error_copy (self->commit_resolve_error);
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+
+gboolean
+_ostree_repo_file_get_xattrs (OstreeRepoFile  *self,
+                              GVariant       **out_xattrs,
+                              GCancellable    *cancellable,
+                              GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *ret_xattrs = NULL;
+  GVariant *metadata = NULL;
+  GInputStream *input = NULL;
+  GFile *local_file = NULL;
+
+  if (!_ostree_repo_file_ensure_resolved (self, error))
+    goto out;
+
+  if (self->tree_metadata)
+    ret_xattrs = g_variant_get_child_value (self->tree_metadata, 4);
+  else if (ostree_repo_is_archive (self->repo))
+    {
+      local_file = _ostree_repo_file_nontree_get_local (self);
+      if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+        goto out;
+      ret_xattrs = g_variant_get_child_value (metadata, 4);
+    }
+  else
+    {
+      local_file = _ostree_repo_file_nontree_get_local (self);
+      ret_xattrs = ostree_get_xattrs_for_path (ot_gfile_get_path_cached (local_file), error);
+    }
+
+  ret = TRUE;
+  *out_xattrs = ret_xattrs;
+  ret_xattrs = NULL;
+ out:
+  if (ret_xattrs)
+    g_variant_unref (ret_xattrs);
+  if (metadata)
+    g_variant_unref (metadata);
+  g_clear_object (&input);
+  g_clear_object (&local_file);
+  return ret;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_contents (OstreeRepoFile  *self)
+{
+  return self->tree_contents;
+}
+
+GVariant *
+_ostree_repo_file_tree_get_metadata (OstreeRepoFile  *self)
+{
+  return self->tree_metadata;
+}
+
+void
+_ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+                                     const char     *checksum,
+                                     GVariant       *metadata)
+{
+  if (self->tree_metadata)
+    g_variant_unref (self->tree_metadata);
+  self->tree_metadata = g_variant_ref (metadata);
+  g_free (self->tree_metadata_checksum);
+  self->tree_metadata_checksum = g_strdup (checksum);
+}
+
+void
+_ostree_repo_file_make_empty_tree (OstreeRepoFile  *self)
+{
+  tree_replace_contents (self, NULL, NULL);
+}
+
+void
+_ostree_repo_file_tree_set_content_checksum (OstreeRepoFile  *self,
+                                             const char      *checksum)
+{
+  g_assert (self->parent == NULL);
+  g_free (self->tree_contents_checksum);
+  self->tree_contents_checksum = g_strdup (checksum);
+}
+
+const char *
+_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile  *self)
+{
+  g_assert (self->parent == NULL);
+  return self->tree_contents_checksum;
+}
+
+GFile *
+_ostree_repo_file_nontree_get_local (OstreeRepoFile  *self)
+{
+  const char *checksum;
+  char *path;
+  GFile *ret;
+
+  g_assert (!ostree_repo_is_archive (self->repo));
+
+  checksum = _ostree_repo_file_nontree_get_checksum (self);
+  path = ostree_repo_get_object_path (self->repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+  ret = ot_util_new_file_for_path (path);
+  g_free (path);
+  
+  return ret;
+}
+
+OstreeRepo *
+_ostree_repo_file_get_repo (OstreeRepoFile  *self)
+{
+  return self->repo;
+}
+
+OstreeRepoFile *
+_ostree_repo_file_get_root (OstreeRepoFile  *self)
+{
+  OstreeRepoFile *parent = self;
+
+  while (parent->parent)
+    parent = parent->parent;
+  return parent;
+}
+
+const char *
+_ostree_repo_file_nontree_get_checksum (OstreeRepoFile  *self)
+{
+  int n;
+  gboolean is_dir;
+
+  g_assert (self->parent);
+
+  n = _ostree_repo_file_tree_find_child (self->parent, self->name, &is_dir, NULL);
+  g_assert (n >= 0 && !is_dir);
+  
+  return _ostree_repo_file_tree_get_child_checksum (self->parent, n);
+}
+
+const char *
+_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile  *self,
+                                           int n)
+{
+  GVariant *files_variant;
+  const char *checksum;
+
+  g_assert (self->tree_contents);
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+  g_variant_get_child (files_variant, n, "(@s&s)", NULL, &checksum);
+
+  g_variant_unref (files_variant);
+
+  return checksum;
+}
+
+static gboolean
+ostree_repo_file_is_native (GFile *file)
+{
+  return FALSE;
+}
+
+static gboolean
+ostree_repo_file_has_uri_scheme (GFile      *file,
+                                const char *uri_scheme)
+{
+  return g_ascii_strcasecmp (uri_scheme, "ostree") == 0;
+}
+
+static char *
+ostree_repo_file_get_uri_scheme (GFile *file)
+{
+  return g_strdup ("ostree");
+}
+
+static char *
+ostree_repo_file_get_basename (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  return g_strdup (self->name);
+}
+
+static char *
+ostree_repo_file_get_path (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  OstreeRepoFile *parent;
+  GString *buf;
+  GSList *parents;
+  GSList *iter;
+
+  buf = g_string_new ("");
+  parents = NULL;
+
+  for (parent = self->parent; parent; parent = parent->parent)
+    parents = g_slist_prepend (parents, parent);
+
+  if (parents->next)
+    {
+      for (iter = parents->next; iter; iter = iter->next)
+        {
+          parent = iter->data;
+          g_string_append_c (buf, '/');
+          g_string_append (buf, parent->name);
+        }
+    }
+  g_string_append_c (buf, '/');
+  g_string_append (buf, self->name);
+
+  g_slist_free (parents);
+
+  return g_string_free (buf, FALSE);
+}
+
+static char *
+ostree_repo_file_get_uri (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  char *path;
+  char *uri_path;
+  char *ret;
+
+  path = g_file_get_path (file);
+  uri_path = g_filename_to_uri (path, NULL, NULL);
+  g_free (path);
+  g_assert (g_str_has_prefix (uri_path, "file://"));
+  ret = g_strconcat ("ostree://", self->commit, uri_path+strlen("file://"), NULL);
+  g_free (uri_path);
+
+  return ret;
+}
+
+static char *
+ostree_repo_file_get_parse_name (GFile *file)
+{
+  return ostree_repo_file_get_uri (file);
+}
+
+static GFile *
+ostree_repo_file_get_parent (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+  return g_object_ref (self->parent);
+}
+
+static GFile *
+ostree_repo_file_dup (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+  if (self->parent)
+    return _ostree_repo_file_new_child (self->parent, self->name);
+  else
+    return _ostree_repo_file_new_root (self->repo, self->commit);
+}
+
+static guint
+ostree_repo_file_hash (GFile *file)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  
+  if (self->parent)
+    return g_file_hash (self->parent) + g_str_hash (self->name);
+  else
+    return g_str_hash (self->commit);
+}
+
+static gboolean
+ostree_repo_file_equal (GFile *file1,
+                        GFile *file2)
+{
+  OstreeRepoFile *self1 = OSTREE_REPO_FILE (file1);
+  OstreeRepoFile *self2 = OSTREE_REPO_FILE (file2);
+
+  if (self1->parent && self2->parent)
+    {
+      return g_str_equal (self1->name, self2->name)
+        && g_file_equal ((GFile*)self1->parent, (GFile*)self2->parent);
+    }
+  else if (!self1->parent && !self2->parent)
+    {
+      return g_str_equal (self1->commit, self2->commit);
+    }
+  else
+    return FALSE;
+}
+
+static const char *
+match_prefix (const char *path, 
+              const char *prefix)
+{
+  int prefix_len;
+
+  prefix_len = strlen (prefix);
+  if (strncmp (path, prefix, prefix_len) != 0)
+    return NULL;
+  
+  /* Handle the case where prefix is the root, so that
+   * the IS_DIR_SEPRARATOR check below works */
+  if (prefix_len > 0 &&
+      G_IS_DIR_SEPARATOR (prefix[prefix_len-1]))
+    prefix_len--;
+  
+  return path + prefix_len;
+}
+
+static gboolean
+ostree_repo_file_prefix_matches (GFile *parent,
+                                GFile *descendant)
+{
+  const char *remainder;
+  char *parent_path;
+  char *descendant_path;
+
+  parent_path = g_file_get_path (parent);
+  descendant_path = g_file_get_path (descendant);
+  remainder = match_prefix (descendant_path, parent_path);
+  g_free (parent_path);
+  g_free (descendant_path);
+  if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+    return TRUE;
+  return FALSE;
+}
+
+static char *
+ostree_repo_file_get_relative_path (GFile *parent,
+                                   GFile *descendant)
+{
+  const char *remainder;
+  char *parent_path;
+  char *descendant_path;
+
+  parent_path = g_file_get_path (parent);
+  descendant_path = g_file_get_path (descendant);
+  remainder = match_prefix (descendant_path, parent_path);
+  g_free (parent_path);
+  g_free (descendant_path);
+  
+  if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+    return g_strdup (remainder + 1);
+  return NULL;
+}
+
+static GFile *
+ostree_repo_file_resolve_relative_path (GFile      *file,
+                                       const char *relative_path)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  OstreeRepoFile *parent;
+  char *filename;
+  const char *rest;
+  GFile *ret;
+
+  if (g_path_is_absolute (relative_path) && self->parent)
+    {
+      g_assert (*relative_path == '/');
+      return ostree_repo_file_resolve_relative_path ((GFile*)_ostree_repo_file_get_root (self),
+                                                     relative_path+1);
+    }
+
+  rest = strchr (relative_path, '/');
+  if (rest)
+    {
+      rest += 1;
+      filename = g_strndup (relative_path, rest - relative_path);
+    }
+  else
+    filename = g_strdup (relative_path);
+
+  parent = (OstreeRepoFile*)_ostree_repo_file_new_child (self, filename);
+  g_free (filename);
+    
+  if (!rest)
+    ret = (GFile*)parent;
+  else
+    {
+      ret = ostree_repo_file_resolve_relative_path ((GFile*)parent, rest);
+      g_clear_object (&parent);
+    }
+  return ret;
+}
+
+static GFileEnumerator *
+ostree_repo_file_enumerate_children (GFile                *file,
+                                    const char           *attributes,
+                                    GFileQueryInfoFlags   flags,
+                                    GCancellable         *cancellable,
+                                    GError              **error)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  return _ostree_repo_file_enumerator_new (self,
+                                          attributes, flags,
+                                          cancellable, error);
+}
+
+static GFile *
+ostree_repo_file_get_child_for_display_name (GFile        *file,
+                                        const char   *display_name,
+                                        GError      **error)
+{
+  return g_file_get_child (file, display_name);
+}
+
+static GFile *
+get_child_local_file (OstreeRepo   *repo,
+                      const char   *checksum)
+{
+  char *path;
+  GFile *ret;
+
+  path = ostree_repo_get_object_path (repo, checksum, OSTREE_OBJECT_TYPE_FILE);
+  ret = ot_util_new_file_for_path (path);
+  g_free (path);
+  
+  return ret;
+}
+
+static gboolean
+query_child_info_file_nonarchive (OstreeRepo       *repo,
+                                  const char       *checksum,
+                                  GFileAttributeMatcher *matcher,
+                                  GFileInfo        *info,
+                                  GCancellable     *cancellable,
+                                  GError          **error)
+{
+  gboolean ret = FALSE;
+  GFileInfo *local_info = NULL;
+  GFile *local_file = NULL;
+  int i ;
+  const char *mapped_boolean[] = {
+    "standard::is-symlink"
+  };
+  const char *mapped_string[] = {
+  };
+  const char *mapped_byte_string[] = {
+    "standard::symlink-target"
+  };
+  const char *mapped_uint32[] = {
+    "standard::type",
+    "unix::device",
+    "unix::mode",
+    "unix::nlink",
+    "unix::uid",
+    "unix::gid",
+    "unix::rdev"
+  };
+  const char *mapped_uint64[] = {
+    "standard::size",
+    "standard::allocated-size",
+    "unix::inode"
+  };
+
+  if (!(g_file_attribute_matcher_matches (matcher, "unix::mode")
+        || g_file_attribute_matcher_matches (matcher, "standard::type")))
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  local_file = get_child_local_file (repo, checksum);
+  local_info = g_file_query_info (local_file,
+                                 OSTREE_GIO_FAST_QUERYINFO,
+                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                 cancellable,
+                                 error);
+  if (!local_info)
+    goto out;
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_boolean); i++)
+    g_file_info_set_attribute_boolean (info, mapped_boolean[i], g_file_info_get_attribute_boolean (local_info, mapped_boolean[i]));
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_string); i++)
+    {
+      const char *string = g_file_info_get_attribute_string (local_info, mapped_string[i]);
+      if (string)
+        g_file_info_set_attribute_string (info, mapped_string[i], string);
+    }
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_byte_string); i++)
+    {
+      const char *byte_string = g_file_info_get_attribute_byte_string (local_info, mapped_byte_string[i]);
+      if (byte_string)
+        g_file_info_set_attribute_byte_string (info, mapped_byte_string[i], byte_string);
+    }
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_uint32); i++)
+    g_file_info_set_attribute_uint32 (info, mapped_uint32[i], g_file_info_get_attribute_uint32 (local_info, mapped_uint32[i]));
+
+  for (i = 0; i < G_N_ELEMENTS (mapped_uint64); i++)
+    g_file_info_set_attribute_uint64 (info, mapped_uint64[i], g_file_info_get_attribute_uint64 (local_info, mapped_uint64[i]));
+  
+  ret = TRUE;
+ out:
+  g_clear_object (&local_info);
+  g_clear_object (&local_file);
+  return ret;
+}
+
+static gboolean
+query_child_info_file_archive (OstreeRepo       *repo,
+                               const char       *checksum,
+                               GFileAttributeMatcher *matcher,
+                               GFileInfo        *info,
+                               GCancellable     *cancellable,
+                               GError          **error)
+{
+  gboolean ret = FALSE;
+  GFile *local_file = NULL;
+  GVariant *metadata = NULL;
+  GInputStream *input = NULL;
+  guint32 version, uid, gid, mode;
+  guint64 content_len;
+  guint32 file_type;
+  gsize bytes_read;
+  char *buf = NULL;
+
+  local_file = get_child_local_file (repo, checksum);
+
+  if (!ostree_parse_packed_file (local_file, &metadata, &input, cancellable, error))
+    goto out;
+
+  g_variant_get (metadata, "(uuuu@a(ayay)t)",
+                 &version, &uid, &gid, &mode,
+                 NULL, &content_len);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+  content_len = GUINT64_FROM_BE (content_len);
+
+  g_file_info_set_attribute_boolean (info, "standard::is-symlink",
+                                     S_ISLNK (mode));
+  if (S_ISLNK (mode))
+    file_type = G_FILE_TYPE_SYMBOLIC_LINK;
+  else if (S_ISREG (mode))
+    file_type = G_FILE_TYPE_REGULAR;
+  else if (S_ISBLK (mode) || S_ISCHR(mode) || S_ISFIFO(mode))
+    file_type = G_FILE_TYPE_SPECIAL;
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted packfile %s: Invalid mode", checksum);
+      goto out;
+    }
+  g_file_info_set_attribute_uint32 (info, "standard::type", file_type);
+
+  g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+  g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+  g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+
+  if (file_type == G_FILE_TYPE_REGULAR)
+    {
+      g_file_info_set_attribute_uint64 (info, "standard::size", content_len);
+    }
+  else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
+    {
+      gsize len = MIN (PATH_MAX, content_len) + 1;
+      buf = g_malloc (len);
+
+      if (!g_input_stream_read_all (input, buf, len, &bytes_read, cancellable, error))
+        goto out;
+      buf[bytes_read] = '\0';
+
+      g_file_info_set_attribute_byte_string (info, "standard::symlink-target", buf);
+    }
+  else if (file_type == G_FILE_TYPE_SPECIAL)
+    {
+      guint32 device;
+
+      if (!g_input_stream_read_all (input, &device, 4, &bytes_read, cancellable, error))
+        goto out;
+
+      device = GUINT32_FROM_BE (device);
+      g_file_info_set_attribute_uint32 (info, "unix::device", device);
+    }
+
+  ret = TRUE;
+ out:
+  g_free (buf);
+  if (metadata)
+    g_variant_unref (metadata);
+  g_clear_object (&local_file);
+  g_clear_object (&input);
+  return ret;
+}
+
+static void
+set_info_from_dirmeta (GFileInfo  *info,
+                       GVariant   *metadata)
+{
+  guint32 version, uid, gid, mode;
+
+  g_file_info_set_attribute_uint32 (info, "standard::type", G_FILE_TYPE_DIRECTORY);
+
+  /* PARSE OSTREE_SERIALIZED_DIRMETA_VARIANT */
+  g_variant_get (metadata, "(uuuu@a(ayay))",
+                 &version, &uid, &gid, &mode,
+                 NULL);
+  version = GUINT32_FROM_BE (version);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+
+  g_file_info_set_attribute_uint32 (info, "unix::uid", uid);
+  g_file_info_set_attribute_uint32 (info, "unix::gid", gid);
+  g_file_info_set_attribute_uint32 (info, "unix::mode", mode);
+}
+
+static gboolean
+query_child_info_dir (OstreeRepo         *repo,
+                      const char         *metadata_checksum,
+                      GFileAttributeMatcher *matcher,
+                      GFileQueryInfoFlags flags,
+                      GFileInfo        *info,
+                      GCancellable    *cancellable,
+                      GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *metadata = NULL;
+
+  if (!g_file_attribute_matcher_matches (matcher, "unix::mode"))
+    {
+      ret = TRUE;
+      goto out;
+    }
+
+  if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_DIRMETA_VARIANT,
+                                         metadata_checksum, &metadata, error))
+    goto out;
+
+  set_info_from_dirmeta (info, metadata);
+  
+  ret = TRUE;
+ out:
+  if (metadata)
+    g_variant_unref (metadata);
+  return ret;
+}
+
+static gboolean
+bsearch_in_file_variant (GVariant  *variant,
+                         const char *name,
+                         int        *out_pos)
+{
+  int i, n;
+  int m;
+
+  i = 0;
+  n = g_variant_n_children (variant) - 1;
+  m = 0;
+
+  while (i <= n)
+    {
+      GVariant *child;
+      const char *cur;
+      int cmp;
+
+      m = i + ((n - i) / 2);
+
+      child = g_variant_get_child_value (variant, m);
+      g_variant_get_child (child, 0, "&s", &cur, NULL);      
+
+      cmp = strcmp (cur, name);
+      if (cmp < 0)
+        i = m + 1;
+      else if (cmp > 0)
+        n = m - 1;
+      else
+        {
+          g_variant_unref (child);
+          *out_pos = m;
+          return TRUE;
+        }
+      g_variant_unref (child);
+    }
+
+  *out_pos = m;
+  return FALSE;
+}
+
+static GVariant *
+remove_variant_child (GVariant *variant,
+                      int       n)
+{
+  GVariantBuilder builder;
+  GVariantIter *iter;
+  int i;
+  GVariant *child;
+
+  g_variant_builder_init (&builder, g_variant_get_type (variant));
+  iter = g_variant_iter_new (variant);
+
+  i = 0;
+  while ((child = g_variant_iter_next_value (iter)) != NULL)
+    {
+      if (i != n)
+        g_variant_builder_add_value (&builder, child);
+      g_variant_unref (child);
+    }
+  g_variant_iter_free (iter);
+  
+  return g_variant_builder_end (&builder);
+}
+
+static GVariant *
+insert_variant_child (GVariant  *variant,
+                      int        n,
+                      GVariant  *item)
+{
+  GVariantBuilder builder;
+  GVariantIter *iter;
+  int i;
+  GVariant *child;
+
+  g_variant_builder_init (&builder, g_variant_get_type (variant));
+  iter = g_variant_iter_new (variant);
+
+  i = 0;
+  while ((child = g_variant_iter_next_value (iter)) != NULL)
+    {
+      if (i == n)
+        g_variant_builder_add_value (&builder, item);
+      g_variant_builder_add_value (&builder, child);
+      g_variant_unref (child);
+    }
+  g_variant_iter_free (iter);
+  
+  return g_variant_builder_end (&builder);
+}
+
+static void
+tree_replace_contents (OstreeRepoFile  *self,
+                       GVariant        *new_files,
+                       GVariant        *new_dirs)
+{
+  guint version;
+  GVariant *metadata;
+  GVariant *tmp_files = NULL;
+  GVariant *tmp_dirs = NULL;
+  
+  if (!(new_files || new_dirs) && self->tree_contents)
+    return;
+  else if (!self->tree_contents)
+    {
+      version = GUINT32_TO_BE (0);
+      metadata = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+      tmp_dirs = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+      tmp_files = g_variant_new_array (G_VARIANT_TYPE ("(ss)"), NULL, 0);
+    }
+  else
+    {
+      g_variant_get_child (self->tree_contents, 0, "u", &version);
+      metadata = g_variant_get_child_value (self->tree_contents, 1);
+      if (!new_files)
+        tmp_files = g_variant_get_child_value (self->tree_contents, 2);
+      if (!new_dirs)
+        tmp_dirs = g_variant_get_child_value (self->tree_contents, 3);
+    }
+
+  if (self->tree_contents)
+    g_variant_unref (self->tree_contents);
+  self->tree_contents = g_variant_new ("(u@a{sv}@a(ss)@a(sss))", version, metadata,
+                                       new_files ? new_files : tmp_files,
+                                       new_dirs ? new_dirs : tmp_dirs);
+
+  g_variant_unref (metadata);
+  if (tmp_files)
+    g_variant_unref (tmp_files);
+  if (tmp_dirs)
+    g_variant_unref (tmp_dirs);
+}
+
+void
+_ostree_repo_file_tree_remove_child (OstreeRepoFile  *self,
+                                     const char      *name)
+{
+  int i;
+  GVariant *files_variant;
+  GVariant *new_files_variant = NULL;
+  GVariant *dirs_variant;
+  GVariant *new_dirs_variant = NULL;
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  if (bsearch_in_file_variant (files_variant, name, &i))
+    {
+      new_files_variant = remove_variant_child (files_variant, i);
+    }
+  else
+    {
+      if (bsearch_in_file_variant (dirs_variant, name, &i))
+        {
+          new_dirs_variant = remove_variant_child (dirs_variant, i);
+        }
+    }
+
+  tree_replace_contents (self, new_files_variant, new_dirs_variant);
+
+  g_variant_unref (files_variant);
+  g_variant_unref (dirs_variant);
+}
+
+void
+_ostree_repo_file_tree_add_file (OstreeRepoFile  *self,
+                                 const char      *name,
+                                 const char      *checksum)
+{
+  int n;
+  GVariant *files_variant;
+  GVariant *new_files_variant;
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+
+  if (!bsearch_in_file_variant (files_variant, name, &n))
+    {
+      new_files_variant = insert_variant_child (files_variant, n,
+                                                g_variant_new ("(ss)", name, checksum));
+      g_variant_ref_sink (new_files_variant);
+      tree_replace_contents (self, new_files_variant, NULL);
+      g_variant_unref (new_files_variant);
+    }
+  g_variant_unref (files_variant);
+}
+
+void
+_ostree_repo_file_tree_add_dir (OstreeRepoFile  *self,
+                                const char      *name,
+                                const char      *content_checksum,
+                                const char      *metadata_checksum)
+{
+  int n;
+  GVariant *dirs_variant;
+  GVariant *new_dirs_variant;
+
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  if (!bsearch_in_file_variant (dirs_variant, name, &n))
+    {
+      new_dirs_variant = insert_variant_child (dirs_variant, n,
+                                               g_variant_new ("(sss)", name, content_checksum,
+                                                              metadata_checksum));
+      g_variant_ref_sink (new_dirs_variant);
+      tree_replace_contents (self, NULL, new_dirs_variant);
+      g_variant_unref (new_dirs_variant);
+    }
+  g_variant_unref (dirs_variant);
+}
+
+int
+_ostree_repo_file_tree_find_child  (OstreeRepoFile  *self,
+                                    const char      *name,
+                                    gboolean        *is_dir,
+                                    GVariant       **out_container)
+{
+  int i;
+  GVariant *files_variant = NULL;
+  GVariant *dirs_variant = NULL;
+  GVariant *ret_container = NULL;
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  i = -1;
+  if (bsearch_in_file_variant (files_variant, name, &i))
+    {
+      *is_dir = FALSE;
+      ret_container = files_variant;
+      files_variant = NULL;
+    }
+  else
+    {
+      if (bsearch_in_file_variant (dirs_variant, name, &i))
+        {
+          *is_dir = TRUE;
+          ret_container = dirs_variant;
+          dirs_variant = NULL;
+        }
+      else
+        {
+          i = -1;
+        }
+    }
+  if (ret_container && out_container)
+    {
+      *out_container = ret_container;
+      ret_container = NULL;
+    }
+  if (ret_container)
+    g_variant_unref (ret_container);
+  if (files_variant)
+    g_variant_unref (files_variant);
+  if (dirs_variant)
+    g_variant_unref (dirs_variant);
+  return i;
+}
+
+gboolean
+_ostree_repo_file_tree_query_child (OstreeRepoFile  *self,
+                                    int              n,
+                                    const char      *attributes,
+                                    GFileQueryInfoFlags flags,
+                                    GFileInfo      **out_info,
+                                    GCancellable    *cancellable,
+                                    GError         **error)
+{
+  const char *name = NULL;
+  gboolean ret = FALSE;
+  GFileInfo *ret_info = NULL;
+  GVariant *files_variant = NULL;
+  GVariant *dirs_variant = NULL;
+  GVariant *tree_child_metadata = NULL;
+  GFileAttributeMatcher *matcher = NULL;
+  int c;
+
+  if (!_ostree_repo_file_ensure_resolved (self, error))
+    goto out;
+
+  matcher = g_file_attribute_matcher_new (attributes);
+
+  ret_info = g_file_info_new ();
+
+  g_assert (self->tree_contents);
+
+  files_variant = g_variant_get_child_value (self->tree_contents, 2);
+  dirs_variant = g_variant_get_child_value (self->tree_contents, 3);
+
+  c = g_variant_n_children (files_variant);
+  if (n < c)
+    {
+      const char *checksum;
+
+      g_variant_get_child (files_variant, n, "(&s&s)", &name, &checksum);
+
+      if (ostree_repo_is_archive (self->repo))
+       {
+         if (!query_child_info_file_archive (self->repo, checksum, matcher, ret_info,
+                                              cancellable, error))
+           goto out;
+       }
+      else
+       {
+         if (!query_child_info_file_nonarchive (self->repo, checksum, matcher, ret_info,
+                                                 cancellable, error))
+           goto out;
+       }
+    }
+  else
+    {
+      const char *tree_checksum;
+      const char *meta_checksum;
+
+      n -= c;
+
+      c = g_variant_n_children (dirs_variant);
+
+      if (n < c)
+        {
+          g_variant_get_child (dirs_variant, n, "(&s&s&s)",
+                               &name, &tree_checksum, &meta_checksum);
+
+          if (!query_child_info_dir (self->repo, meta_checksum,
+                                     matcher, flags, ret_info,
+                                     cancellable, error))
+            goto out;
+        }
+      else
+        n -= c;
+    }
+
+  if (name)
+    {
+      g_file_info_set_attribute_byte_string (ret_info, "standard::name",
+                                             name);
+      g_file_info_set_attribute_string (ret_info, "standard::display-name",
+                                        name);
+      if (*name == '.')
+        g_file_info_set_is_hidden (ret_info, TRUE);
+    }
+  else
+    {
+      g_clear_object (&ret_info);
+    }
+
+  ret = TRUE;
+  *out_info = ret_info;
+  ret_info = NULL;
+ out:
+  g_clear_object (&ret_info);
+  if (matcher)
+    g_file_attribute_matcher_unref (matcher);
+  if (tree_child_metadata)
+    g_variant_unref (tree_child_metadata);
+  g_variant_unref (files_variant);
+  g_variant_unref (dirs_variant);
+  return ret;
+}
+
+static GFileInfo *
+ostree_repo_file_query_info (GFile                *file,
+                            const char           *attributes,
+                            GFileQueryInfoFlags   flags,
+                            GCancellable         *cancellable,
+                            GError              **error)
+{
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+  gboolean ret = FALSE;
+  GFileInfo *info = NULL;
+
+  if (!_ostree_repo_file_ensure_resolved (self, error))
+    goto out;
+
+  if (!self->parent)
+    {
+      info = g_file_info_new ();
+      set_info_from_dirmeta (info, self->tree_metadata);
+    }
+  else
+    {
+      if (!_ostree_repo_file_tree_query_child (self->parent, self->index, 
+                                               attributes, flags, 
+                                               &info, cancellable, error))
+        goto out;
+    }
+      
+  ret = TRUE;
+ out:
+  if (!ret)
+    g_clear_object (&info);
+  return info;
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_settable_attributes (GFile         *file,
+                                       GCancellable  *cancellable,
+                                       GError       **error)
+{
+  return g_file_attribute_info_list_new ();
+}
+
+static GFileAttributeInfoList *
+ostree_repo_file_query_writable_namespaces (GFile         *file,
+                                       GCancellable  *cancellable,
+                                       GError       **error)
+{
+  return g_file_attribute_info_list_new ();
+}
+
+static GFileInputStream *
+ostree_repo_file_read (GFile         *file,
+                      GCancellable  *cancellable,
+                      GError       **error)
+{
+  gboolean ret = FALSE;
+  GFile *local_file = NULL;
+  GFileInputStream *ret_stream = NULL;
+  OstreeRepoFile *self = OSTREE_REPO_FILE (file);
+
+  if (self->tree_contents)
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+                          G_IO_ERROR_IS_DIRECTORY,
+                          "Can't open directory");
+      goto out;
+    }
+
+  if (ostree_repo_is_archive (self->repo))
+    {
+      g_set_error_literal (error, G_IO_ERROR,
+                          G_IO_ERROR_NOT_SUPPORTED,
+                          "Can't open archived file (yet)");
+      goto out;
+    }
+  else
+    {
+      local_file = _ostree_repo_file_nontree_get_local (self);
+      ret_stream = g_file_read (local_file, cancellable, error);
+      if (!ret_stream)
+       goto out;
+    }
+  
+  ret = TRUE;
+ out:
+  g_clear_object (&local_file);
+  if (!ret)
+    g_clear_object (&ret_stream);
+  return ret_stream;
+}
+
+static void
+ostree_repo_file_file_iface_init (GFileIface *iface)
+{
+  iface->dup = ostree_repo_file_dup;
+  iface->hash = ostree_repo_file_hash;
+  iface->equal = ostree_repo_file_equal;
+  iface->is_native = ostree_repo_file_is_native;
+  iface->has_uri_scheme = ostree_repo_file_has_uri_scheme;
+  iface->get_uri_scheme = ostree_repo_file_get_uri_scheme;
+  iface->get_basename = ostree_repo_file_get_basename;
+  iface->get_path = ostree_repo_file_get_path;
+  iface->get_uri = ostree_repo_file_get_uri;
+  iface->get_parse_name = ostree_repo_file_get_parse_name;
+  iface->get_parent = ostree_repo_file_get_parent;
+  iface->prefix_matches = ostree_repo_file_prefix_matches;
+  iface->get_relative_path = ostree_repo_file_get_relative_path;
+  iface->resolve_relative_path = ostree_repo_file_resolve_relative_path;
+  iface->get_child_for_display_name = ostree_repo_file_get_child_for_display_name;
+  iface->set_display_name = NULL;
+  iface->enumerate_children = ostree_repo_file_enumerate_children;
+  iface->query_info = ostree_repo_file_query_info;
+  iface->query_filesystem_info = NULL;
+  iface->find_enclosing_mount = NULL;
+  iface->query_settable_attributes = ostree_repo_file_query_settable_attributes;
+  iface->query_writable_namespaces = ostree_repo_file_query_writable_namespaces;
+  iface->set_attribute = NULL;
+  iface->set_attributes_from_info = NULL;
+  iface->read_fn = ostree_repo_file_read;
+  iface->append_to = NULL;
+  iface->create = NULL;
+  iface->replace = NULL;
+  iface->open_readwrite = NULL;
+  iface->create_readwrite = NULL;
+  iface->replace_readwrite = NULL;
+  iface->delete_file = NULL;
+  iface->trash = NULL;
+  iface->make_directory = NULL;
+  iface->make_symbolic_link = NULL;
+  iface->copy = NULL;
+  iface->move = NULL;
+  iface->monitor_dir = NULL;
+  iface->monitor_file = NULL;
+
+  iface->supports_thread_contexts = TRUE;
+}
diff --git a/src/libostree/ostree-repo-file.h b/src/libostree/ostree-repo-file.h
new file mode 100644 (file)
index 0000000..2074965
--- /dev/null
@@ -0,0 +1,115 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_REPO_FILE
+#define _OSTREE_REPO_FILE
+
+#include "ostree-repo.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO_FILE         (_ostree_repo_file_get_type ())
+#define OSTREE_REPO_FILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFile))
+#define OSTREE_REPO_FILE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+#define OSTREE_IS_LOCAL_FILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_IS_LOCAL_FILE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), OSTREE_TYPE_REPO_FILE))
+#define OSTREE_REPO_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), OSTREE_TYPE_REPO_FILE, OstreeRepoFileClass))
+
+typedef struct _OstreeRepoFile        OstreeRepoFile;
+typedef struct _OstreeRepoFileClass   OstreeRepoFileClass;
+
+struct _OstreeRepoFileClass
+{
+  GObjectClass parent_class;
+};
+
+GType   _ostree_repo_file_get_type (void) G_GNUC_CONST;
+
+GFile * _ostree_repo_file_new_root (OstreeRepo  *repo,
+                                    const char  *commit);
+
+OstreeRepoFile * _ostree_repo_file_new_empty_tree (OstreeRepo  *repo);
+
+GFile * _ostree_repo_file_new_child (OstreeRepoFile *parent,
+                                     const char  *name);
+
+gboolean _ostree_repo_file_ensure_resolved (OstreeRepoFile  *self,
+                                            GError         **error);
+
+gboolean _ostree_repo_file_get_xattrs (OstreeRepoFile  *self,
+                                       GVariant       **out_xattrs,
+                                       GCancellable    *cancellable,
+                                       GError         **error);
+
+OstreeRepo * _ostree_repo_file_get_repo (OstreeRepoFile  *self);
+OstreeRepoFile * _ostree_repo_file_get_root (OstreeRepoFile  *self);
+
+void _ostree_repo_file_make_empty_tree (OstreeRepoFile  *self);
+
+void _ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
+                                          const char     *checksum,
+                                          GVariant       *metadata);
+
+void _ostree_repo_file_tree_set_content_checksum (OstreeRepoFile  *self,
+                                                  const char      *checksum);
+const char *_ostree_repo_file_tree_get_content_checksum (OstreeRepoFile  *self);
+
+gboolean _ostree_repo_file_is_tree (OstreeRepoFile  *self);
+
+const char * _ostree_repo_file_nontree_get_checksum (OstreeRepoFile  *self);
+
+GFile *_ostree_repo_file_nontree_get_local (OstreeRepoFile  *self);
+
+void    _ostree_repo_file_tree_remove_child (OstreeRepoFile  *self,
+                                             const char      *name);
+
+void    _ostree_repo_file_tree_add_file (OstreeRepoFile  *self,
+                                         const char      *name,
+                                         const char      *checksum);
+
+void    _ostree_repo_file_tree_add_dir (OstreeRepoFile  *self,
+                                        const char      *name,
+                                        const char      *content_checksum,
+                                        const char      *metadata_checksum);
+
+int     _ostree_repo_file_tree_find_child  (OstreeRepoFile  *self,
+                                            const char      *name,
+                                            gboolean        *is_dir,
+                                            GVariant       **out_container);
+
+const char *_ostree_repo_file_tree_get_child_checksum (OstreeRepoFile  *self,
+                                                       int n);
+
+gboolean _ostree_repo_file_tree_query_child (OstreeRepoFile  *self,
+                                             int              n,
+                                             const char      *attributes,
+                                             GFileQueryInfoFlags flags,
+                                             GFileInfo      **out_info,
+                                             GCancellable    *cancellable,
+                                             GError         **error);
+
+GVariant *_ostree_repo_file_tree_get_contents (OstreeRepoFile  *self);
+GVariant *_ostree_repo_file_tree_get_metadata (OstreeRepoFile  *self);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
new file mode 100644 (file)
index 0000000..2bdb56d
--- /dev/null
@@ -0,0 +1,1877 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#define _GNU_SOURCE
+
+#include "config.h"
+
+#include "ostree.h"
+#include "otutil.h"
+#include "ostree-repo-file-enumerator.h"
+
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixinputstream.h>
+
+enum {
+  PROP_0,
+
+  PROP_PATH
+};
+
+G_DEFINE_TYPE (OstreeRepo, ostree_repo, G_TYPE_OBJECT)
+
+#define GET_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), OSTREE_TYPE_REPO, OstreeRepoPrivate))
+
+typedef struct _OstreeRepoPrivate OstreeRepoPrivate;
+
+struct _OstreeRepoPrivate {
+  char *path;
+  GFile *repo_file;
+  GFile *tmp_dir;
+  GFile *local_heads_dir;
+  GFile *remote_heads_dir;
+  char *objects_path;
+  char *config_path;
+
+  gboolean inited;
+
+  GKeyFile *config;
+  gboolean archive;
+};
+
+static void
+ostree_repo_finalize (GObject *object)
+{
+  OstreeRepo *self = OSTREE_REPO (object);
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  g_free (priv->path);
+  g_clear_object (&priv->repo_file);
+  g_clear_object (&priv->tmp_dir);
+  g_clear_object (&priv->local_heads_dir);
+  g_clear_object (&priv->remote_heads_dir);
+  g_free (priv->objects_path);
+  g_free (priv->config_path);
+  if (priv->config)
+    g_key_file_free (priv->config);
+
+  G_OBJECT_CLASS (ostree_repo_parent_class)->finalize (object);
+}
+
+static void
+ostree_repo_set_property(GObject         *object,
+                          guint            prop_id,
+                          const GValue    *value,
+                          GParamSpec      *pspec)
+{
+  OstreeRepo *self = OSTREE_REPO (object);
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      priv->path = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_repo_get_property(GObject         *object,
+                          guint            prop_id,
+                          GValue          *value,
+                          GParamSpec      *pspec)
+{
+  OstreeRepo *self = OSTREE_REPO (object);
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  switch (prop_id)
+    {
+    case PROP_PATH:
+      g_value_set_string (value, priv->path);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static GObject *
+ostree_repo_constructor (GType                  gtype,
+                           guint                  n_properties,
+                           GObjectConstructParam *properties)
+{
+  GObject *object;
+  GObjectClass *parent_class;
+  OstreeRepoPrivate *priv;
+
+  parent_class = G_OBJECT_CLASS (ostree_repo_parent_class);
+  object = parent_class->constructor (gtype, n_properties, properties);
+
+  priv = GET_PRIVATE (object);
+
+  g_assert (priv->path != NULL);
+  
+  priv->repo_file = ot_util_new_file_for_path (priv->path);
+  priv->tmp_dir = g_file_resolve_relative_path (priv->repo_file, "tmp");
+  priv->local_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/heads");
+  priv->remote_heads_dir = g_file_resolve_relative_path (priv->repo_file, "refs/remotes");
+  
+  priv->objects_path = g_build_filename (priv->path, "objects", NULL);
+  priv->config_path = g_build_filename (priv->path, "config", NULL);
+
+  return object;
+}
+
+static void
+ostree_repo_class_init (OstreeRepoClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (OstreeRepoPrivate));
+
+  object_class->constructor = ostree_repo_constructor;
+  object_class->get_property = ostree_repo_get_property;
+  object_class->set_property = ostree_repo_set_property;
+  object_class->finalize = ostree_repo_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_PATH,
+                                   g_param_spec_string ("path",
+                                                        "",
+                                                        "",
+                                                        NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_repo_init (OstreeRepo *self)
+{
+}
+
+OstreeRepo*
+ostree_repo_new (const char *path)
+{
+  return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
+}
+
+static gboolean
+parse_rev_file (OstreeRepo     *self,
+                const char     *path,
+                char          **sha256,
+                GError        **error) G_GNUC_UNUSED;
+
+static gboolean
+parse_rev_file (OstreeRepo     *self,
+                const char     *path,
+                char          **sha256,
+                GError        **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GError *temp_error = NULL;
+  gboolean ret = FALSE;
+  char *rev = NULL;
+
+  rev = ot_util_get_file_contents_utf8 (path, &temp_error);
+  if (rev == NULL)
+    {
+      if (g_error_matches (temp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+        {
+          g_clear_error (&temp_error);
+        }
+      else
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+    }
+  else
+    {
+      g_strchomp (rev);
+    }
+
+  if (g_str_has_prefix (rev, "ref: "))
+    {
+      GFile *ref;
+      char *ref_path;
+      char *ref_sha256;
+      gboolean subret;
+
+      ref = g_file_resolve_relative_path (priv->local_heads_dir, rev + 5);
+      ref_path = g_file_get_path (ref);
+
+      subret = parse_rev_file (self, ref_path, &ref_sha256, error);
+      g_clear_object (&ref);
+      g_free (ref_path);
+        
+      if (!subret)
+        {
+          g_free (ref_sha256);
+          goto out;
+        }
+      
+      g_free (rev);
+      rev = ref_sha256;
+    }
+  else 
+    {
+      if (!ostree_validate_checksum_string (rev, error))
+        goto out;
+    }
+
+  *sha256 = rev;
+  rev = NULL;
+  ret = TRUE;
+ out:
+  g_free (rev);
+  return ret;
+}
+
+static gboolean
+resolve_rev (OstreeRepo     *self,
+             const char     *rev,
+             gboolean        allow_noent,
+             char          **sha256,
+             GError        **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  char *tmp = NULL;
+  char *tmp2 = NULL;
+  char *ret_rev = NULL;
+  GFile *child = NULL;
+  char *child_path = NULL;
+  GError *temp_error = NULL;
+  GVariant *commit = NULL;
+
+  if (strlen (rev) == 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid empty rev");
+      goto out;
+    }
+  else if (strlen (rev) == 64)
+    {
+      ret_rev = g_strdup (rev);
+    }
+  else if (g_str_has_suffix (rev, "^"))
+    {
+      tmp = g_strdup (rev);
+      tmp[strlen(tmp) - 1] = '\0';
+
+      if (!resolve_rev (self, tmp, allow_noent, &tmp2, error))
+        goto out;
+
+      if (!ostree_repo_load_variant_checked (self, OSTREE_SERIALIZED_COMMIT_VARIANT, tmp2, &commit, error))
+        goto out;
+      
+      g_variant_get_child (commit, 2, "s", &ret_rev);
+      if (strlen (ret_rev) == 0)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Commit %s has no parent", tmp2);
+          goto out;
+
+        }
+    }
+  else
+    {
+      child = g_file_get_child (priv->local_heads_dir, rev);
+      child_path = g_file_get_path (child);
+      if (!ot_util_gfile_load_contents_utf8 (child, NULL, &ret_rev, NULL, &temp_error))
+        {
+          if (allow_noent && g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+            {
+              g_free (ret_rev);
+              ret_rev = NULL;
+            }
+          else
+            {
+              g_propagate_error (error, temp_error);
+              g_prefix_error (error, "Couldn't open ref '%s': ", child_path);
+              goto out;
+            }
+        }
+      else
+        {
+          g_strchomp (ret_rev);
+         
+          if (!ostree_validate_checksum_string (ret_rev, error))
+            goto out;
+        }
+    }
+
+  *sha256 = ret_rev;
+  ret_rev = NULL;
+  ret = TRUE;
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  g_free (tmp);
+  g_free (tmp2);
+  g_clear_object (&child);
+  g_free (child_path);
+  g_free (ret_rev);
+  return ret;
+}
+
+gboolean
+ostree_repo_resolve_rev (OstreeRepo     *self,
+                         const char     *rev,
+                         char          **sha256,
+                         GError        **error)
+{
+  g_return_val_if_fail (rev != NULL, FALSE);
+  return resolve_rev (self, rev, FALSE, sha256, error);
+}
+
+static gboolean
+write_checksum_file (GFile *parentdir,
+                     const char *name,
+                     const char *sha256,
+                     GError **error)
+{
+  gboolean ret = FALSE;
+  GFile *child = NULL;
+  GOutputStream *out = NULL;
+  gsize bytes_written;
+
+  child = g_file_get_child (parentdir, name);
+
+  if ((out = (GOutputStream*)g_file_replace (child, NULL, FALSE, 0, NULL, error)) == NULL)
+    goto out;
+  if (!g_output_stream_write_all (out, sha256, strlen (sha256), &bytes_written, NULL, error))
+    goto out;
+  if (!g_output_stream_write_all (out, "\n", 1, &bytes_written, NULL, error))
+    goto out;
+  if (!g_output_stream_close (out, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&child);
+  g_clear_object (&out);
+  return ret;
+}
+
+/**
+ * ostree_repo_get_config:
+ * @self:
+ *
+ * Returns: (transfer none): The repository configuration; do not modify
+ */
+GKeyFile *
+ostree_repo_get_config (OstreeRepo *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  g_return_val_if_fail (priv->inited, NULL);
+
+  return priv->config;
+}
+
+/**
+ * ostree_repo_copy_config:
+ * @self:
+ *
+ * Returns: (transfer full): A newly-allocated copy of the repository config
+ */
+GKeyFile *
+ostree_repo_copy_config (OstreeRepo *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GKeyFile *copy;
+  char *data;
+  gsize len;
+
+  g_return_val_if_fail (priv->inited, NULL);
+
+  copy = g_key_file_new ();
+  data = g_key_file_to_data (priv->config, &len, NULL);
+  if (!g_key_file_load_from_data (copy, data, len, 0, NULL))
+    g_assert_not_reached ();
+  g_free (data);
+  return copy;
+}
+
+/**
+ * ostree_repo_write_config:
+ * @self:
+ * @new_config: Overwrite the config file with this data.  Do not change later!
+ * @error: a #GError
+ *
+ * Save @new_config in place of this repository's config file.  Note
+ * that @new_config should not be modified after - this function
+ * simply adds a reference.
+ */
+gboolean
+ostree_repo_write_config (OstreeRepo *self,
+                          GKeyFile   *new_config,
+                          GError    **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  char *data = NULL;
+  gsize len;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  data = g_key_file_to_data (new_config, &len, error);
+  if (!g_file_set_contents (priv->config_path, data, len, error))
+    goto out;
+  
+  g_key_file_free (priv->config);
+  priv->config = g_key_file_new ();
+  if (!g_key_file_load_from_data (priv->config, data, len, 0, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (data);
+  return ret;
+}
+
+gboolean
+ostree_repo_check (OstreeRepo *self, GError **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  char *version = NULL;;
+  GError *temp_error = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (priv->inited)
+    return TRUE;
+
+  if (!g_file_test (priv->objects_path, G_FILE_TEST_IS_DIR))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Couldn't find objects directory '%s'", priv->objects_path);
+      goto out;
+    }
+  
+  priv->config = g_key_file_new ();
+  if (!g_key_file_load_from_file (priv->config, priv->config_path, 0, error))
+    {
+      g_prefix_error (error, "Couldn't parse config file: ");
+      goto out;
+    }
+
+  version = g_key_file_get_value (priv->config, "core", "repo_version", &temp_error);
+  if (temp_error)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  if (strcmp (version, "0") != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid repository version '%s'", version);
+      goto out;
+    }
+
+  priv->archive = g_key_file_get_boolean (priv->config, "core", "archive", &temp_error);
+  if (temp_error)
+    {
+      if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND))
+        {
+          g_clear_error (&temp_error);
+        }
+      else
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+    }
+
+  priv->inited = TRUE;
+  
+  ret = TRUE;
+ out:
+  g_free (version);
+  return ret;
+}
+
+const char *
+ostree_repo_get_path (OstreeRepo  *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  return priv->path;
+}
+
+gboolean      
+ostree_repo_is_archive (OstreeRepo  *self)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  return priv->archive;
+}
+
+static gboolean
+write_gvariant_to_tmp (OstreeRepo  *self,
+                       OstreeSerializedVariantType type,
+                       GVariant    *variant,
+                       GChecksum    **out_checksum,
+                       GError       **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GVariant *serialized = NULL;
+  gboolean ret = FALSE;
+  gsize bytes_written;
+  char *tmp_name = NULL;
+  char *dest_name = NULL;
+  int fd = -1;
+  GUnixOutputStream *stream = NULL;
+  GChecksum *checksum = NULL;
+
+  serialized = g_variant_new ("(uv)", GUINT32_TO_BE ((guint32)type), variant);
+
+  tmp_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir), "variant-tmp-XXXXXX", NULL);
+  fd = g_mkstemp (tmp_name);
+  if (fd < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+  stream = (GUnixOutputStream*)g_unix_output_stream_new (fd, FALSE);
+  if (!g_output_stream_write_all ((GOutputStream*)stream,
+                                  g_variant_get_data (serialized),
+                                  g_variant_get_size (serialized),
+                                  &bytes_written,
+                                  NULL,
+                                  error))
+    goto out;
+
+  g_checksum_update (checksum, (guint8*)g_variant_get_data (serialized), g_variant_get_size (serialized));
+
+  if (!g_output_stream_close ((GOutputStream*)stream,
+                              NULL, error))
+    goto out;
+
+  dest_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir), g_checksum_get_string (checksum), NULL);
+  if (rename (tmp_name, dest_name) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  ret = TRUE;
+  *out_checksum = checksum;
+  checksum = NULL;
+ out:
+  /* Unconditionally unlink; if we suceeded, there's a new link, if not, clean up. */
+  (void) unlink (tmp_name);
+  if (fd != -1)
+    close (fd);
+  if (checksum)
+    g_checksum_free (checksum);
+  if (serialized != NULL)
+    g_variant_unref (serialized);
+  g_free (tmp_name);
+  g_free (dest_name);
+  g_clear_object (&stream);
+  return ret;
+}
+
+static gboolean
+import_gvariant_object (OstreeRepo  *self,
+                        OstreeSerializedVariantType type,
+                        GVariant       *variant,
+                        GChecksum    **out_checksum,
+                        GError       **error)
+{
+  gboolean ret = FALSE;
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  char *tmp_name = NULL;
+  GChecksum *ret_checksum = NULL;
+  gboolean did_exist;
+  
+  if (!write_gvariant_to_tmp (self, type, variant, &ret_checksum, error))
+    goto out;
+
+  tmp_name = g_build_filename (ot_gfile_get_path_cached (priv->tmp_dir),
+                               g_checksum_get_string (ret_checksum), NULL);
+
+  if (!ostree_repo_store_object_trusted (self, tmp_name,
+                                         g_checksum_get_string (ret_checksum),
+                                         OSTREE_OBJECT_TYPE_META,
+                                         TRUE, FALSE, &did_exist, error))
+    goto out;
+
+  ret = TRUE;
+  *out_checksum = ret_checksum;
+  ret_checksum = NULL;
+ out:
+  (void) unlink (tmp_name);
+  g_free (tmp_name);
+  if (ret_checksum)
+    g_checksum_free (ret_checksum);
+  return ret;
+}
+
+gboolean
+ostree_repo_load_variant_checked (OstreeRepo  *self,
+                                  OstreeSerializedVariantType expected_type,
+                                  const char    *sha256, 
+                                  GVariant     **out_variant,
+                                  GError       **error)
+{
+  gboolean ret = FALSE;
+  OstreeSerializedVariantType type;
+  GVariant *ret_variant = NULL;
+
+  if (!ostree_repo_load_variant (self, sha256, &type, &ret_variant, error))
+    goto out;
+
+  if (type != expected_type)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted metadata object '%s'; found type %u, expected %u", sha256,
+                   type, (guint32)expected_type);
+      goto out;
+    }
+
+  ret = TRUE;
+  *out_variant = ret_variant;
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  return ret;
+}
+
+static gboolean
+import_directory_meta (OstreeRepo  *self,
+                       const char *path,
+                       GVariant  **out_variant,
+                       GChecksum **out_checksum,
+                       GError    **error)
+{
+  gboolean ret = FALSE;
+  struct stat stbuf;
+  GChecksum *ret_checksum = NULL;
+  GVariant *dirmeta = NULL;
+  GVariant *xattrs = NULL;
+
+  if (lstat (path, &stbuf) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+  
+  if (!S_ISDIR(stbuf.st_mode))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Not a directory: '%s'", path);
+      goto out;
+    }
+
+  xattrs = ostree_get_xattrs_for_path (path, error);
+  if (!xattrs)
+    goto out;
+
+  dirmeta = g_variant_new ("(uuuu@a(ayay))",
+                           OSTREE_DIR_META_VERSION,
+                           GUINT32_TO_BE ((guint32)stbuf.st_uid),
+                           GUINT32_TO_BE ((guint32)stbuf.st_gid),
+                           GUINT32_TO_BE ((guint32)stbuf.st_mode),
+                           xattrs);
+  g_variant_ref_sink (dirmeta);
+
+  if (!import_gvariant_object (self, OSTREE_SERIALIZED_DIRMETA_VARIANT, 
+                               dirmeta, &ret_checksum, error))
+        goto out;
+
+  ret = TRUE;
+ out:
+  if (!ret)
+    {
+      if (ret_checksum)
+        g_checksum_free (ret_checksum);
+      if (dirmeta != NULL)
+        g_variant_unref (dirmeta);
+    }
+  else
+    {
+      *out_checksum = ret_checksum;
+      *out_variant = dirmeta;
+    }
+  if (xattrs)
+    g_variant_unref (xattrs);
+  return ret;
+}
+
+char *
+ostree_repo_get_object_path (OstreeRepo  *self,
+                             const char    *checksum,
+                             OstreeObjectType type)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  char *ret;
+  char *relpath;
+
+  relpath = ostree_get_relative_object_path (checksum, type, priv->archive);
+  ret = g_build_filename (priv->path, relpath, NULL);
+  g_free (relpath);
+  return ret;
+}
+
+static char *
+prepare_dir_for_checksum_get_object_path (OstreeRepo *self,
+                                          const char   *checksum,
+                                          OstreeObjectType type,
+                                          GError      **error)
+{
+  char *checksum_dir = NULL;
+  char *object_path = NULL;
+
+  object_path = ostree_repo_get_object_path (self, checksum, type);
+  checksum_dir = g_path_get_dirname (object_path);
+
+  if (!ot_util_ensure_directory (checksum_dir, FALSE, error))
+    goto out;
+  
+ out:
+  g_free (checksum_dir);
+  return object_path;
+}
+
+static gboolean
+link_object_trusted (OstreeRepo   *self,
+                     const char   *path,
+                     const char   *checksum,
+                     OstreeObjectType objtype,
+                     gboolean      ignore_exists,
+                     gboolean      force,
+                     gboolean     *did_exist,
+                     GError      **error)
+{
+  char *src_basename = NULL;
+  char *src_dirname = NULL;
+  char *dest_basename = NULL;
+  char *tmp_dest_basename = NULL;
+  char *dest_dirname = NULL;
+  DIR *src_dir = NULL;
+  DIR *dest_dir = NULL;
+  gboolean ret = FALSE;
+  char *dest_path = NULL;
+
+  src_basename = g_path_get_basename (path);
+  src_dirname = g_path_get_dirname (path);
+
+  src_dir = opendir (src_dirname);
+  if (src_dir == NULL)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+  if (!dest_path)
+    goto out;
+
+  dest_basename = g_path_get_basename (dest_path);
+  dest_dirname = g_path_get_dirname (dest_path);
+  dest_dir = opendir (dest_dirname);
+  if (dest_dir == NULL)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  if (force)
+    {
+      tmp_dest_basename = g_strconcat (dest_basename, ".tmp", NULL);
+      (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+    }
+  else
+    tmp_dest_basename = g_strdup (dest_basename);
+  
+  if (linkat (dirfd (src_dir), src_basename, dirfd (dest_dir), tmp_dest_basename, 0) < 0)
+    {
+      if (errno != EEXIST || !ignore_exists)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      else
+        *did_exist = TRUE;
+    }
+  else
+    *did_exist = FALSE;
+
+  if (force)
+    {
+      if (renameat (dirfd (dest_dir), tmp_dest_basename, 
+                    dirfd (dest_dir), dest_basename) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+      (void) unlinkat (dirfd (dest_dir), tmp_dest_basename, 0);
+    }
+
+  ret = TRUE;
+ out:
+  if (src_dir != NULL)
+    closedir (src_dir);
+  if (dest_dir != NULL)
+    closedir (dest_dir);
+  g_free (src_basename);
+  g_free (src_dirname);
+  g_free (dest_basename);
+  g_free (tmp_dest_basename);
+  g_free (dest_dirname);
+  g_free (dest_path);
+  return ret;
+}
+
+static gboolean
+archive_file_trusted (OstreeRepo   *self,
+                      const char   *path,
+                      const char   *checksum,
+                      OstreeObjectType objtype,
+                      gboolean      ignore_exists,
+                      gboolean      force,
+                      gboolean     *did_exist,
+                      GError      **error)
+{
+  GFile *infile = NULL;
+  GFile *outfile = NULL;
+  GFileOutputStream *out = NULL;
+  gboolean ret = FALSE;
+  char *dest_path = NULL;
+  char *dest_tmp_path = NULL;
+
+  infile = ot_util_new_file_for_path (path);
+
+  dest_path = prepare_dir_for_checksum_get_object_path (self, checksum, objtype, error);
+  if (!dest_path)
+    goto out;
+
+  dest_tmp_path = g_strconcat (dest_path, ".tmp", NULL);
+
+  outfile = ot_util_new_file_for_path (dest_tmp_path);
+  out = g_file_replace (outfile, NULL, FALSE, 0, NULL, error);
+  if (!out)
+    goto out;
+
+  if (!ostree_pack_object ((GOutputStream*)out, infile, objtype, NULL, error))
+    goto out;
+  
+  if (!g_output_stream_close ((GOutputStream*)out, NULL, error))
+    goto out;
+
+  if (rename (dest_tmp_path, dest_path) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  g_free (dest_path);
+  g_free (dest_tmp_path);
+  g_clear_object (&infile);
+  g_clear_object (&outfile);
+  g_clear_object (&out);
+  return ret;
+}
+  
+gboolean      
+ostree_repo_store_object_trusted (OstreeRepo   *self,
+                                  const char   *path,
+                                  const char   *checksum,
+                                  OstreeObjectType objtype,
+                                  gboolean      ignore_exists,
+                                  gboolean      force,
+                                  gboolean     *did_exist,
+                                  GError      **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  if (priv->archive && objtype == OSTREE_OBJECT_TYPE_FILE)
+    return archive_file_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+  else
+    return link_object_trusted (self, path, checksum, objtype, ignore_exists, force, did_exist, error);
+}
+
+gboolean
+ostree_repo_store_packfile (OstreeRepo       *self,
+                            const char       *expected_checksum,
+                            const char       *path,
+                            OstreeObjectType  objtype,
+                            GError          **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  GString *tempfile_path = NULL;
+  GChecksum *checksum = NULL;
+  gboolean did_exist;
+
+  tempfile_path = g_string_new (priv->path);
+  g_string_append_printf (tempfile_path, "/tmp-unpack-%s", expected_checksum);
+  
+  if (!ostree_unpack_object (path, objtype, tempfile_path->str, &checksum, error))
+    goto out;
+
+  if (strcmp (g_checksum_get_string (checksum), expected_checksum) != 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Corrupted object %s (actual checksum is %s)",
+                   expected_checksum, g_checksum_get_string (checksum));
+      goto out;
+    }
+
+  if (!ostree_repo_store_object_trusted (self, tempfile_path ? tempfile_path->str : path,
+                                         expected_checksum,
+                                         objtype,
+                                         TRUE, FALSE, &did_exist, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  if (tempfile_path)
+    {
+      (void) unlink (tempfile_path->str);
+      g_string_free (tempfile_path, TRUE);
+    }
+  if (checksum)
+    g_checksum_free (checksum);
+  return ret;
+}
+
+typedef struct _ParsedTreeData ParsedTreeData;
+typedef struct _ParsedDirectoryData ParsedDirectoryData;
+
+static void parsed_tree_data_free (ParsedTreeData *pdata);
+
+struct _ParsedDirectoryData {
+  ParsedTreeData *tree_data;
+  char *metadata_sha256;
+  GVariant *meta_data;
+};
+
+static void
+parsed_directory_data_free (ParsedDirectoryData *pdata)
+{
+  if (pdata == NULL)
+    return;
+  parsed_tree_data_free (pdata->tree_data);
+  g_free (pdata->metadata_sha256);
+  g_variant_unref (pdata->meta_data);
+  g_free (pdata);
+}
+
+struct _ParsedTreeData {
+  GHashTable *files;  /* char* filename -> char* checksum */
+  GHashTable *directories;  /* char* dirname -> ParsedDirectoryData* */
+};
+
+static ParsedTreeData *
+parsed_tree_data_new (void)
+{
+  ParsedTreeData *ret = g_new0 (ParsedTreeData, 1);
+  ret->files = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                      (GDestroyNotify)g_free, 
+                                      (GDestroyNotify)g_free);
+  ret->directories = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                            (GDestroyNotify)g_free, 
+                                            (GDestroyNotify)parsed_directory_data_free);
+  return ret;
+}
+
+static void
+parsed_tree_data_free (ParsedTreeData *pdata)
+{
+  if (pdata == NULL)
+    return;
+  g_hash_table_destroy (pdata->files);
+  g_hash_table_destroy (pdata->directories);
+  g_free (pdata);
+}
+
+static GVariant *
+create_empty_gvariant_dict (void)
+{
+  GVariantBuilder builder;
+  g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
+  return g_variant_builder_end (&builder);
+}
+
+static gboolean
+import_parsed_tree (OstreeRepo    *self,
+                    ParsedTreeData  *tree,
+                    GChecksum      **out_checksum,
+                    GError         **error)
+{
+  gboolean ret = FALSE;
+  GVariant *serialized_tree = NULL;
+  gboolean builders_initialized = FALSE;
+  GVariantBuilder files_builder;
+  GVariantBuilder dirs_builder;
+  GHashTableIter hash_iter;
+  GSList *sorted_filenames = NULL;
+  GSList *iter;
+  gpointer key, value;
+
+  g_variant_builder_init (&files_builder, G_VARIANT_TYPE ("a(ss)"));
+  g_variant_builder_init (&dirs_builder, G_VARIANT_TYPE ("a(sss)"));
+  builders_initialized = TRUE;
+
+  g_hash_table_iter_init (&hash_iter, tree->files);
+  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+    {
+      const char *name = key;
+      sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
+    }
+
+  sorted_filenames = g_slist_sort (sorted_filenames, (GCompareFunc)strcmp);
+
+  for (iter = sorted_filenames; iter; iter = iter->next)
+    {
+      const char *name = iter->data;
+      const char *value;
+
+      value = g_hash_table_lookup (tree->files, name);
+      g_variant_builder_add (&files_builder, "(ss)", name, value);
+    }
+
+  g_slist_free (sorted_filenames);
+  sorted_filenames = NULL;
+
+  g_hash_table_iter_init (&hash_iter, tree->directories);
+  while (g_hash_table_iter_next (&hash_iter, &key, &value))
+    {
+      const char *name = key;
+      sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
+    }
+
+  sorted_filenames = g_slist_sort (sorted_filenames, (GCompareFunc)strcmp);
+
+  for (iter = sorted_filenames; iter; iter = iter->next)
+    {
+      const char *name = iter->data;
+      GChecksum *dir_checksum = NULL;
+      ParsedDirectoryData *dir;
+
+      dir = g_hash_table_lookup (tree->directories, name);
+
+      if (!import_parsed_tree (self, dir->tree_data, &dir_checksum, error))
+        goto out;
+
+      g_variant_builder_add (&dirs_builder, "(sss)",
+                             name, g_checksum_get_string (dir_checksum), dir->metadata_sha256);
+      g_checksum_free (dir_checksum);
+    }
+
+  g_slist_free (sorted_filenames);
+  sorted_filenames = NULL;
+
+  serialized_tree = g_variant_new ("(u@a{sv}@a(ss)@a(sss))",
+                                   GUINT32_TO_BE (0),
+                                   create_empty_gvariant_dict (),
+                                   g_variant_builder_end (&files_builder),
+                                   g_variant_builder_end (&dirs_builder));
+  builders_initialized = FALSE;
+  g_variant_ref_sink (serialized_tree);
+  if (!import_gvariant_object (self, OSTREE_SERIALIZED_TREE_VARIANT, serialized_tree, out_checksum, error))
+    goto out;
+  
+  ret = TRUE;
+ out:
+  g_slist_free (sorted_filenames);
+  if (builders_initialized)
+    {
+      g_variant_builder_clear (&files_builder);
+      g_variant_builder_clear (&dirs_builder);
+    }
+  if (serialized_tree)
+    g_variant_unref (serialized_tree);
+  return ret;
+}
+
+static gboolean
+check_path (const char *filename,
+            GError    **error)
+{
+  gboolean ret = FALSE;
+
+  if (!*filename)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid empty filename");
+      goto out;
+    }
+
+  if (strcmp (filename, ".") == 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Self-reference '.' in filename '%s' not allowed (yet)", filename);
+      goto out;
+    }
+  
+  if (ot_util_filename_has_dotdot (filename))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Path uplink '..' in filename '%s' not allowed (yet)", filename);
+      goto out;
+    }
+  
+  if (g_path_is_absolute (filename))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Absolute filename '%s' not allowed (yet)", filename);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+add_one_directory_to_tree_and_import (OstreeRepo   *self,
+                                      const char     *basename,
+                                      const char     *abspath,
+                                      ParsedTreeData *tree,
+                                      ParsedDirectoryData **dir, /*inout*/
+                                      GError        **error)
+{
+  gboolean ret = FALSE;
+  GVariant *dirmeta = NULL;
+  GChecksum *dir_meta_checksum = NULL;
+  ParsedDirectoryData *dir_value = *dir;
+  
+  g_assert (tree != NULL);
+
+  if (!import_directory_meta (self, abspath, &dirmeta, &dir_meta_checksum, error))
+    goto out;
+
+  if (dir_value)
+    {
+      g_variant_unref (dir_value->meta_data);
+      dir_value->meta_data = dirmeta;
+    }
+  else
+    {
+      dir_value = g_new0 (ParsedDirectoryData, 1);
+      dir_value->tree_data = parsed_tree_data_new ();
+      dir_value->metadata_sha256 = g_strdup (g_checksum_get_string (dir_meta_checksum));
+      dir_value->meta_data = dirmeta;
+      g_hash_table_insert (tree->directories, g_strdup (basename), dir_value);
+    }
+
+  ret = TRUE;
+  *dir = dir_value;
+ out:
+  if (dir_meta_checksum)
+    g_checksum_free (dir_meta_checksum);
+  return ret;
+}
+
+static gboolean
+add_one_file_to_tree_and_import (OstreeRepo   *self,
+                                 const char     *basename,
+                                 const char     *abspath,
+                                 ParsedTreeData *tree,
+                                 GError        **error)
+{
+  gboolean ret = FALSE;
+  GChecksum *checksum = NULL;
+  struct stat stbuf;
+  gboolean did_exist;
+  
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_assert (tree != NULL);
+
+  if (!ostree_stat_and_checksum_file (-1, abspath, OSTREE_OBJECT_TYPE_FILE, &checksum, &stbuf, error))
+    goto out;
+
+  if (!ostree_repo_store_object_trusted (self, abspath, g_checksum_get_string (checksum),
+                                         OSTREE_OBJECT_TYPE_FILE, TRUE, FALSE, &did_exist, error))
+    goto out;
+
+  g_hash_table_replace (tree->files, g_strdup (basename),
+                        g_strdup (g_checksum_get_string (checksum)));
+
+  ret = TRUE;
+ out:
+  if (checksum)
+    g_checksum_free (checksum);
+  return ret;
+}
+
+static gboolean
+add_one_path_to_tree_and_import (OstreeRepo     *self,
+                                 const char     *base,
+                                 const char     *filename,
+                                 ParsedTreeData *tree,
+                                 GError        **error)
+{
+  gboolean ret = FALSE;
+  GPtrArray *components = NULL;
+  struct stat stbuf;
+  char *component_abspath = NULL;
+  ParsedTreeData *current_tree = tree;
+  const char *component = NULL;
+  const char *file_sha1;
+  char *abspath = NULL;
+  ParsedDirectoryData *dir;
+  int i;
+  gboolean is_directory;
+
+  if (!check_path (filename, error))
+    goto out;
+
+  abspath = g_build_filename (base, filename, NULL);
+
+  if (lstat (abspath, &stbuf) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+  is_directory = S_ISDIR(stbuf.st_mode);
+       
+  if (components)
+    g_ptr_array_free (components, TRUE);
+  components = ot_util_path_split (filename);
+  g_assert (components->len > 0);
+
+  current_tree = tree;
+  for (i = 0; i < components->len; i++)
+    {
+      component = components->pdata[i];
+      g_free (component_abspath);
+      component_abspath = ot_util_path_join_n (base, components, i);
+      file_sha1 = g_hash_table_lookup (current_tree->files, component);
+      dir = g_hash_table_lookup (current_tree->directories, component);
+
+      g_assert_cmpstr (component, !=, ".");
+
+      if (i < components->len - 1)
+        {
+          if (file_sha1 != NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Encountered non-directory '%s' in '%s'",
+                           component,
+                           filename);
+              goto out;
+            }
+          /* Implicitly add intermediate directories */
+          if (!add_one_directory_to_tree_and_import (self, component,
+                                                     component_abspath, current_tree, &dir,
+                                                     error))
+            goto out;
+          g_assert (dir != NULL);
+          current_tree = dir->tree_data;
+        }
+      else if (is_directory)
+        {
+          if (file_sha1 != NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "File '%s' can't be overwritten by directory",
+                           filename);
+              goto out;
+            }
+          if (!add_one_directory_to_tree_and_import (self, component,
+                                                     abspath, current_tree, &dir,
+                                                     error))
+            goto out;
+        }
+      else 
+        {
+          g_assert (!is_directory);
+          if (dir != NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "File '%s' can't be overwritten by directory",
+                           filename);
+              goto out;
+            }
+          if (!add_one_file_to_tree_and_import (self, component, abspath,
+                                                current_tree, error))
+            goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  if (components)
+    g_ptr_array_unref (components);
+  g_free (component_abspath);
+  g_free (abspath);
+  return ret;
+}
+
+gboolean      
+ostree_repo_write_ref (OstreeRepo  *self,
+                       gboolean     is_local,
+                       const char  *name,
+                       const char  *rev,
+                       GError     **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  return write_checksum_file (is_local ? priv->local_heads_dir : priv->remote_heads_dir, 
+                              name, rev, error);
+}
+
+static gboolean
+commit_parsed_tree (OstreeRepo *self,
+                    const char   *branch,
+                    const char   *parent,
+                    const char   *subject,
+                    const char   *body,
+                    GVariant     *metadata,
+                    ParsedDirectoryData *root,
+                    GChecksum   **out_commit,
+                    GError      **error)
+{
+  gboolean ret = FALSE;
+  GChecksum *root_checksum = NULL;
+  GChecksum *ret_commit = NULL;
+  GVariant *commit = NULL;
+  GDateTime *now = NULL;
+
+  g_assert (branch != NULL);
+  g_assert (subject != NULL);
+
+  if (!import_parsed_tree (self, root->tree_data, &root_checksum, error))
+    goto out;
+
+  now = g_date_time_new_now_utc ();
+  commit = g_variant_new ("(u@a{sv}ssstss)",
+                          GUINT32_TO_BE (OSTREE_COMMIT_VERSION),
+                          metadata ? metadata : create_empty_gvariant_dict (),
+                          parent ? parent : "",
+                          subject, body ? body : "",
+                          GUINT64_TO_BE (g_date_time_to_unix (now)),
+                          g_checksum_get_string (root_checksum),
+                          root->metadata_sha256);
+  g_variant_ref_sink (commit);
+  if (!import_gvariant_object (self, OSTREE_SERIALIZED_COMMIT_VARIANT,
+                               commit, &ret_commit, error))
+    goto out;
+
+  if (!ostree_repo_write_ref (self, TRUE, branch, g_checksum_get_string (ret_commit), error))
+    goto out;
+
+  ret = TRUE;
+  *out_commit = ret_commit;
+ out:
+  if (root_checksum)
+    g_checksum_free (root_checksum);
+  if (commit)
+    g_variant_unref (commit);
+  if (now)
+    g_date_time_unref (now);
+  return ret;
+}
+
+static gboolean
+import_root (OstreeRepo           *self,
+             const char           *base,
+             ParsedDirectoryData **out_root,
+             GError              **error)
+{
+  gboolean ret = FALSE;
+  ParsedDirectoryData *ret_root = NULL;
+  GVariant *root_metadata = NULL;
+  GChecksum *root_meta_checksum = NULL;
+
+  if (!import_directory_meta (self, base, &root_metadata, &root_meta_checksum, error))
+    goto out;
+
+  ret_root = g_new0 (ParsedDirectoryData, 1);
+  ret_root->tree_data = parsed_tree_data_new ();
+  ret_root->meta_data = root_metadata;
+  root_metadata = NULL;
+  ret_root->metadata_sha256 = g_strdup (g_checksum_get_string (root_meta_checksum));
+
+  ret = TRUE;
+  *out_root = ret_root;
+  ret_root = NULL;
+ out:
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  if (root_meta_checksum)
+    g_checksum_free (root_meta_checksum);
+  parsed_directory_data_free (ret_root);
+  return ret;
+}
+
+gboolean      
+ostree_repo_commit_from_filelist_fd (OstreeRepo *self,
+                                     const char   *branch,
+                                     const char   *parent,
+                                     const char   *subject,
+                                     const char   *body,
+                                     GVariant     *metadata,
+                                     const char   *base,
+                                     int           fd,
+                                     char          separator,
+                                     GChecksum   **out_commit,
+                                     GError      **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  ParsedDirectoryData *root = NULL;
+  GChecksum *ret_commit_checksum = NULL;
+  GUnixInputStream *in = NULL;
+  GDataInputStream *datain = NULL;
+  char *filename = NULL;
+  gsize filename_len;
+  GError *temp_error = NULL;
+  GVariant *root_metadata = NULL;
+  GChecksum *root_meta_checksum = NULL;
+  char *current_head = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (priv->inited, FALSE);
+  g_return_val_if_fail (branch != NULL, FALSE);
+  g_return_val_if_fail (subject != NULL, FALSE);
+  g_return_val_if_fail (metadata == NULL || g_variant_is_of_type (metadata, G_VARIANT_TYPE ("a{sv}")), FALSE);
+
+  if (parent == NULL)
+    parent = branch;
+
+  /* We're overwriting the tree */
+  if (!import_root (self, base, &root, error))
+    goto out;
+
+  if (!resolve_rev (self, parent, TRUE, &current_head, error))
+    goto out;
+
+  in = (GUnixInputStream*)g_unix_input_stream_new (fd, FALSE);
+  datain = g_data_input_stream_new ((GInputStream*)in);
+
+  while ((filename = g_data_input_stream_read_upto (datain, &separator, 1,
+                                                    &filename_len, NULL, &temp_error)) != NULL)
+    {
+      if (!g_data_input_stream_read_byte (datain, NULL, &temp_error))
+        {
+          if (temp_error != NULL)
+            {
+              g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+              goto out;
+            }
+        }
+      if (!add_one_path_to_tree_and_import (self, base, filename, root->tree_data, error))
+        goto out;
+      g_free (filename);
+      filename = NULL;
+    }
+  if (filename == NULL && temp_error != NULL)
+    {
+      g_propagate_prefixed_error (error, temp_error, "%s", "While reading filelist: ");
+      goto out;
+    }
+  if (!commit_parsed_tree (self, branch, current_head, subject, body, metadata,
+                           root, &ret_commit_checksum, error))
+    goto out;
+  
+  ret = TRUE;
+  *out_commit = ret_commit_checksum;
+  ret_commit_checksum = NULL;
+ out:
+  if (ret_commit_checksum)
+    g_checksum_free (ret_commit_checksum);
+  g_free (current_head);
+  if (root_metadata)
+    g_variant_unref (root_metadata);
+  if (root_meta_checksum)
+    g_checksum_free (root_meta_checksum);
+  g_clear_object (&datain);
+  g_clear_object (&in);
+  g_free (filename);
+  parsed_directory_data_free (root);
+  return ret;
+  
+}
+
+static gboolean
+iter_object_dir (OstreeRepo   *self,
+                 GFile          *dir,
+                 OstreeRepoObjectIter  callback,
+                 gpointer                user_data,
+                 GError                **error)
+{
+  gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  GFileEnumerator *enumerator = NULL;
+  GFileInfo *file_info = NULL;
+  char *dirpath = NULL;
+
+  dirpath = g_file_get_path (dir);
+
+  enumerator = g_file_enumerate_children (dir, OSTREE_GIO_FAST_QUERYINFO, 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+  
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      if (type != G_FILE_TYPE_DIRECTORY
+          && (g_str_has_suffix (name, ".meta")
+              || g_str_has_suffix (name, ".file")
+              || g_str_has_suffix (name, ".packfile")))
+        {
+          char *dot;
+          char *path;
+          
+          dot = strrchr (name, '.');
+          g_assert (dot);
+          
+          if ((dot - name) == 62)
+            {
+              path = g_build_filename (dirpath, name, NULL);
+              callback (self, path, file_info, user_data);
+              g_free (path);
+            }
+        }
+
+      g_object_unref (file_info);
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (!g_file_enumerator_close (enumerator, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (dirpath);
+  return ret;
+}
+
+gboolean
+ostree_repo_iter_objects (OstreeRepo  *self,
+                            OstreeRepoObjectIter callback,
+                            gpointer       user_data,
+                            GError        **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  GFile *objectdir = NULL;
+  GFileEnumerator *enumerator = NULL;
+  gboolean ret = FALSE;
+  GFileInfo *file_info = NULL;
+  GError *temp_error = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (priv->inited, FALSE);
+
+  objectdir = ot_util_new_file_for_path (priv->objects_path);
+  enumerator = g_file_enumerate_children (objectdir, OSTREE_GIO_FAST_QUERYINFO, 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          NULL, 
+                                          error);
+  if (!enumerator)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+      
+      if (strlen (name) == 2 && type == G_FILE_TYPE_DIRECTORY)
+        {
+          GFile *objdir = g_file_get_child (objectdir, name);
+          if (!iter_object_dir (self, objdir, callback, user_data, error))
+            {
+              g_object_unref (objdir);
+              goto out;
+            }
+          g_object_unref (objdir);
+        }
+      g_object_unref (file_info);
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+  if (!g_file_enumerator_close (enumerator, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&file_info);
+  g_clear_object (&enumerator);
+  g_clear_object (&objectdir);
+  return ret;
+}
+
+gboolean
+ostree_repo_load_variant (OstreeRepo *self,
+                          const char   *sha256,
+                          OstreeSerializedVariantType *out_type,
+                          GVariant    **out_variant,
+                          GError      **error)
+{
+  gboolean ret = FALSE;
+  OstreeSerializedVariantType ret_type;
+  GVariant *ret_variant = NULL;
+  char *path = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  path = ostree_repo_get_object_path (self, sha256, OSTREE_OBJECT_TYPE_META);
+  if (!ostree_parse_metadata_file (path, &ret_type, &ret_variant, error))
+    goto out;
+
+  ret = TRUE;
+  *out_type = ret_type;
+  *out_variant = ret_variant;
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  g_free (path);
+  return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo    *self,
+               OstreeRepoFile *dir,
+               const char      *destination,
+               GCancellable    *cancellable,
+               GError         **error);
+
+static gboolean
+checkout_one_directory (OstreeRepo  *self,
+                        const char *destination,
+                        const char *dirname,
+                        OstreeRepoFile *dir,
+                        GFileInfo      *dir_info,
+                        GCancellable    *cancellable,
+                        GError         **error)
+{
+  gboolean ret = FALSE;
+  char *dest_path = NULL;
+  GVariant *xattr_variant = NULL;
+
+  dest_path = g_build_filename (destination, dirname, NULL);
+
+  if (!_ostree_repo_file_get_xattrs (dir, &xattr_variant, NULL, error))
+    goto out;
+
+  if (mkdir (dest_path, (mode_t)g_file_info_get_attribute_uint32 (dir_info, "unix::mode")) < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      g_prefix_error (error, "Failed to create directory '%s': ", dest_path);
+      goto out;
+    }
+
+  if (!ostree_set_xattrs (dest_path, xattr_variant, cancellable, error))
+    goto out;
+      
+  if (!checkout_tree (self, dir, dest_path, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (dest_path);
+  if (xattr_variant)
+    g_variant_unref (xattr_variant);
+  return ret;
+}
+
+static gboolean
+checkout_tree (OstreeRepo    *self,
+               OstreeRepoFile *dir,
+               const char      *destination,
+               GCancellable    *cancellable,
+               GError         **error)
+{
+  OstreeRepoPrivate *priv = GET_PRIVATE (self);
+  gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  GFileInfo *file_info = NULL;
+  GFileEnumerator *dir_enum = NULL;
+  GFile *child = NULL;
+  char *object_path = NULL;
+  char *dest_path = NULL;
+
+  dir_enum = g_file_enumerate_children ((GFile*)dir, OSTREE_GIO_FAST_QUERYINFO, 
+                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                        cancellable, 
+                                        error);
+  if (!dir_enum)
+    goto out;
+
+  while ((file_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error)) != NULL)
+    {
+      const char *name;
+      guint32 type;
+
+      name = g_file_info_get_attribute_byte_string (file_info, "standard::name"); 
+      type = g_file_info_get_attribute_uint32 (file_info, "standard::type");
+
+      child = g_file_get_child ((GFile*)dir, name);
+
+      if (type == G_FILE_TYPE_DIRECTORY)
+        {
+          if (!checkout_one_directory (self, destination, name, (OstreeRepoFile*)child, file_info, cancellable, error))
+            goto out;
+        }
+      else
+        {
+          const char *checksum = _ostree_repo_file_nontree_get_checksum ((OstreeRepoFile*)child);
+
+          dest_path = g_build_filename (destination, name, NULL);
+          object_path = ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
+
+          if (priv->archive)
+            {
+              if (!ostree_unpack_object (object_path, OSTREE_OBJECT_TYPE_FILE, dest_path, NULL, error))
+                goto out;
+            }
+          else
+            {
+              if (link (object_path, dest_path) < 0)
+                {
+                  ot_util_set_error_from_errno (error, errno);
+                  goto out;
+                }
+            }
+        }
+
+      g_free (object_path);
+      object_path = NULL;
+      g_free (dest_path);
+      dest_path = NULL;
+      g_clear_object (&file_info);
+      g_clear_object (&child);
+    }
+  if (file_info == NULL && temp_error != NULL)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  g_clear_object (&dir_enum);
+  g_clear_object (&file_info);
+  g_clear_object (&child);
+  g_free (object_path);
+  g_free (dest_path);
+  return ret;
+}
+
+gboolean
+ostree_repo_checkout (OstreeRepo *self,
+                      const char   *rev,
+                      const char   *destination,
+                      GCancellable *cancellable,
+                      GError      **error)
+{
+  gboolean ret = FALSE;
+  char *resolved = NULL;
+  OstreeRepoFile *root = NULL;
+  GFileInfo *root_info = NULL;
+
+  if (g_file_test (destination, G_FILE_TEST_EXISTS))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Destination path '%s' already exists",
+                   destination);
+      goto out;
+    }
+
+  if (!resolve_rev (self, rev, FALSE, &resolved, error))
+    goto out;
+
+  root = (OstreeRepoFile*)_ostree_repo_file_new_root (self, resolved);
+  if (!_ostree_repo_file_ensure_resolved (root, error))
+    goto out;
+
+  root_info = g_file_query_info ((GFile*)root, OSTREE_GIO_FAST_QUERYINFO,
+                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                 NULL, error);
+  if (!root_info)
+    goto out;
+
+  if (!checkout_one_directory (self, destination, NULL, root, root_info, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (resolved);
+  g_clear_object (&root);
+  g_clear_object (&root_info);
+  return ret;
+}
+
+gboolean
+ostree_repo_diff (OstreeRepo     *self,
+                  const char     *ref,
+                  GFile          *target,
+                  GPtrArray     **out_modified,
+                  GPtrArray     **out_removed,
+                  GPtrArray     **out_added,
+                  GCancellable   *cancellable,
+                  GError        **error)
+{
+  gboolean ret = FALSE;
+  GPtrArray *ret_modified = NULL;
+  GPtrArray *ret_removed = NULL;
+  GPtrArray *ret_added = NULL;
+
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+               "Not implemented yet");
+  goto out;
+
+  ret = TRUE;
+ out:
+  if (ret_modified)
+    g_ptr_array_free (ret_modified, TRUE);
+  if (ret_removed)
+    g_ptr_array_free (ret_removed, TRUE);
+  if (ret_added)
+    g_ptr_array_free (ret_added, TRUE);
+  return ret;
+}
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
new file mode 100644 (file)
index 0000000..47a896e
--- /dev/null
@@ -0,0 +1,162 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef _OSTREE_REPO
+#define _OSTREE_REPO
+
+#include "ostree-core.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_REPO ostree_repo_get_type()
+#define OSTREE_REPO(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_REPO, OstreeRepo))
+#define OSTREE_REPO_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), OSTREE_TYPE_REPO, OstreeRepoClass))
+#define OSTREE_IS_REPO(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO))
+#define OSTREE_IS_REPO_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), OSTREE_TYPE_REPO))
+#define OSTREE_REPO_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), OSTREE_TYPE_REPO, OstreeRepoClass))
+
+typedef struct {
+  GObject parent;
+} OstreeRepo;
+
+typedef struct {
+  GObjectClass parent_class;
+} OstreeRepoClass;
+
+GType ostree_repo_get_type (void);
+
+OstreeRepo* ostree_repo_new (const char *path);
+
+gboolean      ostree_repo_check (OstreeRepo  *self, GError **error);
+
+const char *  ostree_repo_get_path (OstreeRepo  *self);
+
+gboolean      ostree_repo_is_archive (OstreeRepo  *self);
+
+GKeyFile *    ostree_repo_get_config (OstreeRepo *self);
+
+GKeyFile *    ostree_repo_copy_config (OstreeRepo *self);
+
+gboolean      ostree_repo_write_config (OstreeRepo *self,
+                                        GKeyFile   *new_config,
+                                        GError    **error);
+
+char *        ostree_repo_get_object_path (OstreeRepo   *self,
+                                           const char   *object,
+                                           OstreeObjectType type);
+
+gboolean      ostree_repo_store_packfile (OstreeRepo       *self,
+                                           const char       *expected_checksum,
+                                           const char       *path,
+                                           OstreeObjectType  objtype,
+                                           GError          **error);
+
+gboolean      ostree_repo_store_object_trusted (OstreeRepo   *self,
+                                                const char   *path,
+                                                const char   *checksum,
+                                                OstreeObjectType objtype,
+                                                gboolean      ignore_exists,
+                                                gboolean      force,
+                                                gboolean     *did_exist,
+                                                GError      **error);
+
+gboolean      ostree_repo_resolve_rev (OstreeRepo  *self,
+                                       const char  *rev,
+                                       char       **out_resolved,
+                                       GError     **error);
+
+gboolean      ostree_repo_write_ref (OstreeRepo  *self,
+                                     gboolean     is_local,
+                                     const char  *name,
+                                     const char  *rev,
+                                     GError     **error);
+
+gboolean      ostree_repo_load_variant (OstreeRepo *self,
+                                          const char   *sha256,
+                                          OstreeSerializedVariantType *out_type,
+                                          GVariant    **out_variant,
+                                          GError      **error);
+
+gboolean      ostree_repo_load_variant_checked (OstreeRepo  *self,
+                                                OstreeSerializedVariantType expected_type,
+                                                const char    *sha256, 
+                                                GVariant     **out_variant,
+                                                GError       **error);
+
+gboolean      ostree_repo_commit_from_filelist_fd (OstreeRepo   *self,
+                                                   const char   *branch,
+                                                   const char   *parent,
+                                                   const char   *subject,
+                                                   const char   *body,
+                                                   GVariant     *metadata,
+                                                   const char   *base,
+                                                   int           fd,
+                                                   char          separator,
+                                                   GChecksum   **out_commit,
+                                                   GError      **error);
+
+gboolean      ostree_repo_checkout (OstreeRepo *self,
+                                    const char   *ref,
+                                    const char   *destination,
+                                    GCancellable   *cancellable,
+                                    GError      **error);
+
+typedef struct {
+  guint content_differs : 1;
+  guint xattrs_differs : 1;
+  guint unused : 30;
+
+  GFileInfo *src_info;
+  GFileInfo *target_info;
+
+  char *src_file_checksum;
+  char *target_file_checksum;
+
+  GVariant *src_xattrs;
+  GVariant *target_xattrs;
+} OstreeRepoDiffItem;
+
+gboolean      ostree_repo_diff (OstreeRepo     *self,
+                                const char     *ref,
+                                GFile          *target,
+                                GPtrArray     **out_modified, /* OstreeRepoDiffItem */
+                                GPtrArray     **out_removed, /* OstreeRepoDiffItem */
+                                GPtrArray     **out_added, /* OstreeRepoDiffItem */
+                                GCancellable   *cancellable,
+                                GError        **error);
+
+typedef void (*OstreeRepoObjectIter) (OstreeRepo *self, const char *path,
+                                        GFileInfo *fileinfo, gpointer user_data);
+
+gboolean     ostree_repo_iter_objects (OstreeRepo  *self,
+                                         OstreeRepoObjectIter callback,
+                                         gpointer       user_data,
+                                         GError        **error);
+
+G_END_DECLS
+
+#endif /* _OSTREE_REPO */
diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h
new file mode 100644 (file)
index 0000000..d683080
--- /dev/null
@@ -0,0 +1,29 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_H__
+
+#include <ostree-core.h>
+#include <ostree-repo.h>
+#include <ostree-checkout.h>
+
+#endif
diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c
new file mode 100644 (file)
index 0000000..bae8037
--- /dev/null
@@ -0,0 +1,178 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+gboolean
+ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error)
+{
+  GFile *dir;
+  GError *temp_error = NULL;
+  gboolean ret = FALSE;
+
+  dir = g_file_new_for_path (path);
+  if (with_parents)
+    ret = g_file_make_directory_with_parents (dir, NULL, &temp_error);
+  else
+    ret = g_file_make_directory (dir, NULL, &temp_error);
+  if (!ret)
+    {
+      if (!g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+      else
+        g_clear_error (&temp_error);
+    }
+
+  ret = TRUE;
+ out:
+  g_clear_object (&dir);
+  return ret;
+}
+
+
+char *
+ot_util_get_file_contents_utf8 (const char *path,
+                                GError    **error)
+{
+  GFile *file = NULL;
+  char *ret = NULL;
+
+  file = ot_util_new_file_for_path (path);
+  if (!ot_util_gfile_load_contents_utf8 (file, NULL, &ret, NULL, error))
+    goto out;
+
+ out:
+  g_clear_object (&file);
+  return ret;
+}
+
+gboolean
+ot_util_gfile_load_contents_utf8 (GFile         *file,
+                                  GCancellable  *cancellable,
+                                  char         **contents_out,
+                                  char         **etag_out,
+                                  GError       **error)
+{
+  char *ret_contents = NULL;
+  char *ret_etag = NULL;
+  gsize len;
+  gboolean ret = FALSE;
+
+  if (!g_file_load_contents (file, cancellable, &ret_contents, &len, &ret_etag, error))
+    goto out;
+  if (!g_utf8_validate (ret_contents, len, NULL))
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_DATA,
+                   "Invalid UTF-8");
+      goto out;
+    }
+
+  if (contents_out)
+    *contents_out = ret_contents;
+  else
+    g_free (ret_contents);
+  ret_contents = NULL;
+  if (etag_out)
+    *etag_out = ret_etag;
+  else
+    g_free (ret_etag);
+  ret_etag = NULL;
+  ret = TRUE;
+ out:
+  g_free (ret_contents);
+  g_free (ret_etag);
+  return ret;
+}
+
+GInputStream *
+ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error)
+{
+  GInputStream *ret = NULL;
+  int fd;
+  int flags = O_RDONLY;
+  char *path = NULL;
+
+  path = g_file_get_path (file);
+#ifdef O_NOATIME
+  flags |= O_NOATIME;
+#endif
+  fd = open (path, flags);
+  if (fd < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  ret = (GInputStream*)g_unix_input_stream_new (fd, TRUE);
+  
+ out:
+  g_free (path);
+  return ret;
+}
+
+/* Like g_file_new_for_path, but only do local stuff, not GVFS */
+GFile *
+ot_util_new_file_for_path (const char *path)
+{
+  return g_vfs_get_file_for_path (g_vfs_get_local (), path);
+}
+
+const char *
+ot_gfile_get_path_cached (GFile *file)
+{
+  const char *path;
+
+  path = g_object_get_data ((GObject*)file, "ostree-file-path");
+  if (!path)
+    {
+      path = g_file_get_path (file);
+      g_object_set_data_full ((GObject*)file, "ostree-file-path", (char*)path, (GDestroyNotify)g_free);
+    }
+  return path;
+}
+
+
+const char *
+ot_gfile_get_basename_cached (GFile *file)
+{
+  const char *name;
+
+  name = g_object_get_data ((GObject*)file, "ostree-file-name");
+  if (!name)
+    {
+      name = g_file_get_basename (file);
+      g_object_set_data_full ((GObject*)file, "ostree-file-name", (char*)name, (GDestroyNotify)g_free);
+    }
+  return name;
+}
diff --git a/src/libotutil/ot-gio-utils.h b/src/libotutil/ot-gio-utils.h
new file mode 100644 (file)
index 0000000..8435b87
--- /dev/null
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GIO_UTILS_H__
+#define __OSTREE_GIO_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+GFile *ot_util_new_file_for_path (const char *path);
+
+const char *ot_gfile_get_path_cached (GFile *file);
+
+const char *ot_gfile_get_basename_cached (GFile *file);
+
+gboolean ot_util_ensure_directory (const char *path, gboolean with_parents, GError **error);
+
+char * ot_util_get_file_contents_utf8 (const char *path, GError    **error);
+
+gboolean ot_util_gfile_load_contents_utf8 (GFile         *file,
+                                           GCancellable  *cancellable,
+                                           char         **contents_out,
+                                           char         **etag_out,
+                                           GError       **error);
+
+GInputStream *ot_util_read_file_noatime (GFile *file, GCancellable *cancellable, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libotutil/ot-glib-compat.c b/src/libotutil/ot-glib-compat.c
new file mode 100644 (file)
index 0000000..44181b9
--- /dev/null
@@ -0,0 +1,160 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+#if GLIB_CHECK_VERSION(2,32,0)
+/* nothing */
+#else
+/* Code copied from glib/glib/genviron.c */
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1998  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+static gint
+ot_g_environ_find (gchar       **envp,
+                const gchar  *variable)
+{
+  gint len, i;
+
+  len = strlen (variable);
+
+  for (i = 0; envp[i]; i++)
+    {
+      if (strncmp (envp[i], variable, len) == 0 &&
+          envp[i][len] == '=')
+        return i;
+    }
+
+  return -1;
+}
+
+const gchar *
+ot_g_environ_getenv (gchar       **envp,
+                  const gchar  *variable)
+{
+  gint index;
+
+  g_return_val_if_fail (envp != NULL, NULL);
+  g_return_val_if_fail (variable != NULL, NULL);
+
+  index = ot_g_environ_find (envp, variable);
+  if (index != -1)
+    return envp[index] + strlen (variable) + 1;
+  else
+    return NULL;
+}
+
+gchar **
+ot_g_environ_setenv (gchar       **envp,
+                  const gchar  *variable,
+                  const gchar  *value,
+                  gboolean      overwrite)
+{
+  gint index;
+
+  g_return_val_if_fail (envp != NULL, NULL);
+  g_return_val_if_fail (variable != NULL, NULL);
+  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+
+  index = ot_g_environ_find (envp, variable);
+  if (index != -1)
+    {
+      if (overwrite)
+        {
+          g_free (envp[index]);
+          envp[index] = g_strdup_printf ("%s=%s", variable, value);
+        }
+    }
+  else
+    {
+      gint length;
+
+      length = g_strv_length (envp);
+      envp = g_renew (gchar *, envp, length + 2);
+      envp[length] = g_strdup_printf ("%s=%s", variable, value);
+      envp[length + 1] = NULL;
+    }
+
+  return envp;
+}
+
+gchar **
+ot_g_environ_unsetenv (gchar       **envp,
+                    const gchar  *variable)
+{
+  gint len;
+  gchar **e, **f;
+
+  g_return_val_if_fail (envp != NULL, NULL);
+  g_return_val_if_fail (variable != NULL, NULL);
+  g_return_val_if_fail (strchr (variable, '=') == NULL, NULL);
+
+  len = strlen (variable);
+
+  /* Note that we remove *all* environment entries for
+   * the variable name, not just the first.
+   */
+  e = f = envp;
+  while (*e != NULL)
+    {
+      if (strncmp (*e, variable, len) != 0 || (*e)[len] != '=')
+        {
+          *f = *e;
+          f++;
+        }
+      e++;
+    }
+  *f = NULL;
+
+  return envp;
+}
+
+#endif
diff --git a/src/libotutil/ot-glib-compat.h b/src/libotutil/ot-glib-compat.h
new file mode 100644 (file)
index 0000000..18d7d3d
--- /dev/null
@@ -0,0 +1,53 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_GLIB_COMPAT_H__
+#define __OSTREE_GLIB_COMPAT_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#if GLIB_CHECK_VERSION(2,32,0)
+#define ot_g_environ_getenv g_environ_getenv
+#define ot_g_environ_setenv g_environ_setenv
+#define ot_g_environ_unsetenv g_environ_unsetenv
+#else
+const gchar *
+ot_g_environ_getenv (gchar       **envp,
+                     const gchar  *variable);
+
+gchar **
+ot_g_environ_setenv (gchar       **envp,
+                     const gchar  *variable,
+                     const gchar  *value,
+                     gboolean      overwrite);
+
+gchar **
+ot_g_environ_unsetenv (gchar       **envp,
+                       const gchar  *variable);
+#endif
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/libotutil/ot-opt-utils.c b/src/libotutil/ot-opt-utils.c
new file mode 100644 (file)
index 0000000..750e5b6
--- /dev/null
@@ -0,0 +1,38 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+void
+ot_util_usage_error (GOptionContext *context, const char *message, GError **error)
+{
+  gchar *help = g_option_context_get_help (context, TRUE, NULL);
+  g_printerr ("%s\n", help);
+  g_free (help);
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message);
+}
diff --git a/src/libotutil/ot-opt-utils.h b/src/libotutil/ot-opt-utils.h
new file mode 100644 (file)
index 0000000..fdcfd33
--- /dev/null
@@ -0,0 +1,34 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_OPT_UTILS_H__
+#define __OSTREE_OPT_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+void ot_util_usage_error (GOptionContext *context, const char *message, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libotutil/ot-unix-utils.c b/src/libotutil/ot-unix-utils.c
new file mode 100644 (file)
index 0000000..24fcf8b
--- /dev/null
@@ -0,0 +1,273 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-unix-utils.h"
+
+#include <gio/gio.h>
+#include <gio/gunixoutputstream.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+
+gboolean
+ot_util_spawn_pager (GOutputStream  **out_stream,
+                     GError         **error)
+{
+  const char *pager;
+  char *argv[2];
+  int stdin_fd;
+  pid_t pid;
+  gboolean ret = FALSE;
+  GOutputStream *ret_stream = NULL;
+
+  if (!isatty (1))
+    {
+      ret_stream = (GOutputStream*)g_unix_output_stream_new (1, TRUE);
+    }
+  else
+    {
+      pager = g_getenv ("GIT_PAGER");
+      if (pager == NULL)
+        pager = "less";
+      
+      argv[0] = (char*)pager;
+      argv[1] = NULL;
+      
+      if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                     NULL, NULL, &pid, &stdin_fd, NULL, NULL, error))
+        {
+          g_prefix_error (error, "%s", "Failed to spawn pager: ");
+          goto out;
+        }
+      
+      ret_stream = (GOutputStream*)g_unix_output_stream_new (stdin_fd, TRUE);
+    }
+
+  *out_stream = ret_stream;
+  ret_stream = NULL;
+  ret = TRUE;
+ out:
+  g_clear_object (&ret_stream);
+  return ret;
+}
+
+static int
+compare_filenames_by_component_length (const char *a,
+                                       const char *b)
+{
+  char *a_slash, *b_slash;
+
+  a_slash = strchr (a, '/');
+  b_slash = strchr (b, '/');
+  while (a_slash && b_slash)
+    {
+      a = a_slash + 1;
+      b = b_slash + 1;
+      a_slash = strchr (a, '/');
+      b_slash = strchr (b, '/');
+    }
+  if (a_slash)
+    return -1;
+  else if (b_slash)
+    return 1;
+  else
+    return 0;
+}
+
+GPtrArray *
+ot_util_sort_filenames_by_component_length (GPtrArray *files)
+{
+  GPtrArray *array = g_ptr_array_sized_new (files->len);
+  memcpy (array->pdata, files->pdata, sizeof (gpointer) * files->len);
+  g_ptr_array_sort (array, (GCompareFunc) compare_filenames_by_component_length);
+  return array;
+}
+
+int
+ot_util_count_filename_components (const char *path)
+{
+  int i = 0;
+
+  while (path)
+    {
+      i++;
+      path = strchr (path, '/');
+      if (path)
+        path++;
+    }
+  return i;
+}
+
+gboolean
+ot_util_filename_has_dotdot (const char *path)
+{
+  char *p;
+  char last;
+
+  if (strcmp (path, "..") == 0)
+    return TRUE;
+  if (g_str_has_prefix (path, "../"))
+    return TRUE;
+  p = strstr (path, "/..");
+  if (!p)
+    return FALSE;
+  last = *(p + 1);
+  return last == '\0' || last == '/';
+}
+
+GPtrArray *
+ot_util_path_split (const char *path)
+{
+  GPtrArray *ret = NULL;
+  const char *p;
+  const char *slash;
+  int i;
+
+  g_return_val_if_fail (path[0] != '/', NULL);
+
+  ret = g_ptr_array_new ();
+  g_ptr_array_set_free_func (ret, g_free);
+
+  p = path;
+  do {
+    slash = strchr (p, '/');
+    if (!slash)
+      {
+        g_ptr_array_add (ret, g_strdup (p));
+        p = NULL;
+      }
+    else
+      {
+        g_ptr_array_add (ret, g_strndup (p, slash - p));
+        p = slash + 1;
+      }
+  } while (p && *p);
+
+  /* Canonicalize by removing duplicate '.' */
+  for (i = ret->len-1; i >= 0; i--)
+    {
+      if (strcmp (ret->pdata[i], ".") == 0)
+        g_ptr_array_remove_index (ret, i);
+    }
+
+  return ret;
+}
+
+char *
+ot_util_path_join_n (const char *base, GPtrArray *components, int n)
+{
+  int max = MIN(n+1, components->len);
+  GPtrArray *subcomponents;
+  char *path;
+  int i;
+
+  subcomponents = g_ptr_array_new ();
+
+  if (base != NULL)
+    g_ptr_array_add (subcomponents, (char*)base);
+
+  for (i = 0; i < max; i++)
+    {
+      g_ptr_array_add (subcomponents, components->pdata[i]);
+    }
+  g_ptr_array_add (subcomponents, NULL);
+  
+  path = g_build_filenamev ((char**)subcomponents->pdata);
+  g_ptr_array_free (subcomponents, TRUE);
+  
+  return path;
+}
+
+void
+ot_util_set_error_from_errno (GError **error,
+                              gint     saved_errno)
+{
+  g_set_error_literal (error,
+                       G_IO_ERROR,
+                       0,
+                       g_strerror (saved_errno));
+  errno = saved_errno;
+}
+
+int
+ot_util_open_file_read (const char *path, GError **error)
+{
+  char *dirname = NULL;
+  char *basename = NULL;
+  DIR *dir = NULL;
+  int fd = -1;
+
+  dirname = g_path_get_dirname (path);
+  basename = g_path_get_basename (path);
+  dir = opendir (dirname);
+  if (dir == NULL)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+
+  fd = ot_util_open_file_read_at (dirfd (dir), basename, error);
+
+ out:
+  g_free (basename);
+  g_free (dirname);
+  if (dir != NULL)
+    closedir (dir);
+  return fd;
+}
+
+int
+ot_util_open_file_read_at (int dirfd, const char *name, GError **error)
+{
+  int fd;
+  int flags = O_RDONLY;
+  
+#ifdef O_CLOEXEC
+  flags |= O_CLOEXEC;
+#endif
+#ifdef O_NOATIME
+  flags |= O_NOATIME;
+#endif
+  fd = openat (dirfd, name, flags);
+  if (fd < 0)
+    ot_util_set_error_from_errno (error, errno);
+  return fd;
+}
+
+void
+ot_util_fatal_literal (const char *msg)
+{
+  g_printerr ("%s\n", msg);
+  exit (1);
+}
+
+void
+ot_util_fatal_gerror (GError *error)
+{
+  g_assert (error != NULL);
+  ot_util_fatal_literal (error->message);
+}
diff --git a/src/libotutil/ot-unix-utils.h b/src/libotutil/ot-unix-utils.h
new file mode 100644 (file)
index 0000000..8b392cb
--- /dev/null
@@ -0,0 +1,64 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UNIX_UTILS_H__
+#define __OSTREE_UNIX_UTILS_H__
+
+#include <gio/gio.h>
+
+/* I just put all this shit here. Sue me. */
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+G_BEGIN_DECLS
+
+gboolean ot_util_spawn_pager (GOutputStream  **out_stream, GError         **error);
+
+void ot_util_fatal_literal (const char *msg) G_GNUC_NORETURN;
+
+void ot_util_fatal_gerror (GError *error) G_GNUC_NORETURN;
+
+gboolean ot_util_filename_has_dotdot (const char *path);
+
+GPtrArray *ot_util_sort_filenames_by_component_length (GPtrArray *files);
+
+GPtrArray* ot_util_path_split (const char *path);
+
+char *ot_util_path_join_n (const char *base, GPtrArray *components, int n);
+
+int ot_util_count_filename_components (const char *path);
+
+int ot_util_open_file_read (const char *path, GError **error);
+
+int ot_util_open_file_read_at (int dirfd, const char *name, GError **error);
+
+void ot_util_set_error_from_errno (GError **error, gint saved_errno);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libotutil/ot-variant-utils.c b/src/libotutil/ot-variant-utils.c
new file mode 100644 (file)
index 0000000..d450ed6
--- /dev/null
@@ -0,0 +1,126 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "otutil.h"
+
+GHashTable *
+ot_util_variant_asv_to_hash_table (GVariant *variant)
+{
+  GHashTable *ret;
+  GVariantIter *viter;
+  char *key;
+  GVariant *value;
+  
+  ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
+  viter = g_variant_iter_new (variant);
+  while (g_variant_iter_next (viter, "{s@v}", &key, &value))
+    g_hash_table_replace (ret, key, g_variant_ref_sink (value));
+  
+  g_variant_iter_free (viter);
+  
+  return ret;
+}
+
+gboolean
+ot_util_variant_save (GFile *dest,
+                      GVariant *variant,
+                      GCancellable *cancellable,
+                      GError  **error)
+{
+  gboolean ret = FALSE;
+  GOutputStream *out = NULL;
+  gsize bytes_written;
+  
+  out = (GOutputStream*)g_file_replace (dest, NULL, 0, FALSE, cancellable, error);
+  if (!out)
+    goto out;
+
+  if (!g_output_stream_write_all (out,
+                                  g_variant_get_data (variant),
+                                  g_variant_get_size (variant),
+                                  &bytes_written,
+                                  cancellable,
+                                  error))
+    goto out;
+  if (!g_output_stream_close (out, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&out);
+  return ret;
+}
+
+GVariant *
+ot_util_variant_take_ref (GVariant *variant)
+{
+#if GLIB_CHECK_VERSION(2,32,0)
+  return g_variant_take_ref (variant);
+#else
+  if (g_variant_is_floating (variant))
+    return g_variant_ref_sink (variant);
+  return variant;
+#endif
+}
+
+gboolean
+ot_util_variant_map (GFile *src,
+                     const GVariantType *type,
+                     GVariant **out_variant,
+                     GError  **error)
+{
+  gboolean ret = FALSE;
+  GMappedFile *mfile = NULL;
+  char *path = NULL;
+  GVariant *ret_variant = NULL;
+
+  path = g_file_get_path (src);
+  mfile = g_mapped_file_new (path, FALSE, error);
+  if (!mfile)
+    goto out;
+
+  ret_variant = g_variant_new_from_data (type,
+                                         g_mapped_file_get_contents (mfile),
+                                         g_mapped_file_get_length (mfile),
+                                         FALSE,
+                                         (GDestroyNotify) g_mapped_file_unref,
+                                         mfile);
+  mfile = NULL;
+  g_variant_ref_sink (ret_variant);
+  
+  ret = TRUE;
+  *out_variant = ret_variant;
+  ret_variant = NULL;
+ out:
+  if (ret_variant)
+    g_variant_unref (ret_variant);
+  if (mfile)
+    g_mapped_file_unref (mfile);
+  g_free (path);
+  return ret;
+}
diff --git a/src/libotutil/ot-variant-utils.h b/src/libotutil/ot-variant-utils.h
new file mode 100644 (file)
index 0000000..57ce663
--- /dev/null
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_VARIANT_UTILS_H__
+#define __OSTREE_VARIANT_UTILS_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+GHashTable *ot_util_variant_asv_to_hash_table (GVariant *variant);
+
+GVariant * ot_util_variant_take_ref (GVariant *variant);
+
+gboolean ot_util_variant_save (GFile *dest,
+                               GVariant *variant,
+                               GCancellable *cancellable,
+                               GError  **error);
+
+gboolean ot_util_variant_map (GFile *src,
+                              const GVariantType *type,
+                              GVariant **out_variant,
+                              GError  **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libotutil/otutil.h b/src/libotutil/otutil.h
new file mode 100644 (file)
index 0000000..a93dc84
--- /dev/null
@@ -0,0 +1,31 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_UTIL_H__
+
+#include <ot-gio-utils.h>
+#include <ot-glib-compat.h>
+#include <ot-opt-utils.h>
+#include <ot-unix-utils.h>
+#include <ot-variant-utils.h>
+
+#endif
diff --git a/src/osbuild/main.c b/src/osbuild/main.c
new file mode 100644 (file)
index 0000000..d481e99
--- /dev/null
@@ -0,0 +1,108 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "ob-builtins.h"
+
+static OsbuildBuiltin builtins[] = {
+  { "buildone", osbuild_builtin_buildone, 0 },
+  { NULL }
+};
+
+static int
+usage (char **argv, gboolean is_error)
+{
+  OsbuildBuiltin *builtin = builtins;
+  void (*print_func) (const gchar *format, ...);
+
+  if (is_error)
+    print_func = g_printerr;
+  else
+    print_func = g_print;
+
+  print_func ("usage: %s COMMAND [options]\n",
+              argv[0]);
+  print_func ("Builtin commands:\n");
+
+  while (builtin->name)
+    {
+      print_func ("  %s\n", builtin->name);
+      builtin++;
+    }
+  return (is_error ? 1 : 0);
+}
+
+
+int
+main (int    argc,
+      char **argv)
+{
+  OsbuildBuiltin *builtin;
+  const char *cmd;
+
+  g_type_init ();
+
+  g_set_prgname (argv[0]);
+
+  builtin = builtins;
+
+  if (argc < 2)
+    return usage (argv, 1);
+  
+  cmd = argv[1];
+
+  while (builtin->name)
+    {
+      GError *error = NULL;
+      if (strcmp (cmd, builtin->name) == 0)
+        {
+          int i;
+          int tmp_argc;
+          char **tmp_argv;
+
+          tmp_argc = argc - 1;
+          tmp_argv = g_new0 (char *, tmp_argc + 1);
+
+          tmp_argv[0] = (char*)builtin->name;
+          for (i = 0; i < tmp_argc; i++)
+            tmp_argv[i+1] = argv[i+2];
+          if (!builtin->fn (tmp_argc, tmp_argv, NULL, &error))
+            {
+              g_free (tmp_argv);
+              g_printerr ("%s\n", error->message);
+              g_clear_error (&error);
+              return 1;
+            }
+          g_free (tmp_argv);
+          return 0;
+        }
+      builtin++;
+    }
+  
+  g_printerr ("Unknown command '%s'\n", cmd);
+  return usage (argv, 1);
+}
diff --git a/src/osbuild/ob-builtin-buildone.c b/src/osbuild/ob-builtin-buildone.c
new file mode 100644 (file)
index 0000000..1387563
--- /dev/null
@@ -0,0 +1,142 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "otutil.h"
+#include "ob-builtins.h"
+
+#include <glib/gi18n.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *repo_path;
+static char *ref;
+static char *name;
+static char *generator;
+static char *resultdir;
+static gboolean raw;
+
+static GOptionEntry options[] = {
+  { "repo", 0, 0, G_OPTION_ARG_FILENAME, &repo_path, "Repository path", "repo" },
+  { "rev", 'r', 0, G_OPTION_ARG_STRING, &ref, "Build using this tree", "rev" },
+  { "name", 0, 0, G_OPTION_ARG_STRING, &name, "Name of the source", "source" },
+  { "generator", 0, 0, G_OPTION_ARG_FILENAME, &generator, "Script to run on installed tree", "script" },
+  { "raw", 0, 0, G_OPTION_ARG_NONE, &raw, "Do not instantiate a tree, use current", NULL },
+  { "resultdir", 0, 0, G_OPTION_ARG_FILENAME, &resultdir, "Directory for output artifacts", "dir" },
+  { NULL }
+};
+
+static char *
+get_tmpdir (void) G_GNUC_UNUSED;
+
+static char *
+get_tmpdir (void)
+{
+  char *tmp_prefix = g_strdup (g_getenv ("XDG_RUNTIME_DIR"));
+  char *ret;
+  
+  if (tmp_prefix)
+    {
+      ret = g_strdup_printf ("%s/osbuild", tmp_prefix);
+    }
+  else
+    {
+      tmp_prefix = g_strdup_printf ("/tmp/osbuild-%d", getuid ());
+      if (!g_file_test (tmp_prefix, G_FILE_TEST_IS_DIR))
+        {
+          if (!mkdir (tmp_prefix, 0755))
+            {
+              g_printerr ("Failed to make logging directory %s\n", tmp_prefix);
+              exit (1);
+            }
+        }
+      ret = tmp_prefix;
+      tmp_prefix = NULL;
+    }
+  g_free (tmp_prefix);
+  return ret;
+}
+
+static gboolean
+open_log (const char *name, 
+          GOutputStream **out_log,
+          GError **error) G_GNUC_UNUSED;
+
+static gboolean
+open_log (const char *name, 
+          GOutputStream **out_log,
+          GError **error)
+{
+  gboolean ret = FALSE;
+  char *tmpdir = NULL;
+  char *path = NULL;
+  GFile *logf = NULL;
+  GFileOutputStream *ret_log = NULL;
+
+  path = g_strdup_printf ("%s/%s.log", tmpdir, name);
+  logf = ot_util_new_file_for_path (path);
+
+  ret_log = g_file_replace (logf, NULL, FALSE, 0, NULL, error);
+  if (!ret_log)
+    goto out;
+
+  ret = TRUE;
+  *out_log = (GOutputStream*)ret_log;
+  ret_log = NULL;
+ out:
+  g_free (path);
+  g_free (tmpdir);
+  g_clear_object (&logf);
+  g_clear_object (&ret_log);
+  return ret;
+}
+
+gboolean
+osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+
+  context = g_option_context_new ("- Build current directory");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (!raw && !repo_path)
+    {
+      ot_util_usage_error (context, "A result directory must be specified with --resultdir", error);
+      goto out;
+    }
+
+  if (!generator)
+    generator = g_build_filename (LIBEXECDIR, "ostree", "generators", "default", NULL);
+
+  
+
+ out:
+  if (context)
+    g_option_context_free (context);
+  return ret;
+}
diff --git a/src/osbuild/ob-builtins.h b/src/osbuild/ob-builtins.h
new file mode 100644 (file)
index 0000000..800039b
--- /dev/null
@@ -0,0 +1,44 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSBUILD_BUILTINS__
+#define __OSBUILD_BUILTINS__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  OSBUILD_BUILTIN_FLAG_NONE = 0,
+} OsbuildBuiltinFlags;
+
+typedef struct {
+  const char *name;
+  gboolean (*fn) (int argc, char **argv, const char *prefix, GError **error);
+  int flags; /* OsbuildBuiltinFlags */
+} OsbuildBuiltin;
+
+gboolean osbuild_builtin_buildone (int argc, char **argv, const char *prefix, GError **error);
+
+G_END_DECLS
+
+#endif
diff --git a/src/osbuild/osbuild-raw-makeinstall.c b/src/osbuild/osbuild-raw-makeinstall.c
new file mode 100644 (file)
index 0000000..a404ffe
--- /dev/null
@@ -0,0 +1,188 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include "otutil.h"
+
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static const char *
+find_first_file (GFileTest test, const char *name, ...) G_GNUC_NULL_TERMINATED;
+
+static const char *
+find_first_file (GFileTest test, const char *name, ...)
+{
+  va_list args;
+
+  va_start (args, name);
+
+  do
+    {
+      if (g_file_test (name, test))
+        break;
+      name = va_arg (args, const char *);
+    }
+  while (name != NULL);
+
+  va_end (args);
+
+  return name;
+}
+
+static void
+split_configure_make_args (int argc,
+                           char **argv,
+                           GPtrArray **out_configure_args,
+                           GPtrArray **out_make_args,
+                           GPtrArray **out_makeinstall_args)
+{
+  int i;
+
+  *out_configure_args = g_ptr_array_new ();
+  *out_make_args = g_ptr_array_new ();
+  *out_makeinstall_args = g_ptr_array_new ();
+
+  for (i = 1; i < argc; i++)
+    {
+      if (g_str_has_prefix (argv[i], "--"))
+        g_ptr_array_add (*out_configure_args, argv[i]);
+      else if (g_str_has_prefix (argv[i], "DESTDIR="))
+        g_ptr_array_add (*out_makeinstall_args, argv[i]);
+      else
+        g_ptr_array_add (*out_make_args, argv[i]);
+    }
+}
+
+static void
+spawn_sync_or_fatal (char **args, char **env, GSpawnFlags flags)
+{
+  GError *error = NULL;
+  int estatus;
+  char **iter;
+
+  g_print ("osbuild: running: ");
+  for (iter = args; *iter; iter++)
+    g_print ("%s ", *iter);
+  g_print ("\n");
+  if (g_spawn_sync (NULL, args, env, flags, NULL, NULL, NULL, NULL, &estatus, &error))
+    { 
+      if (WIFEXITED (estatus) && WEXITSTATUS (estatus) == 0)
+        {
+          g_message ("Subprocess %s exited successfully\n", args[0]);
+        }
+      else
+        {
+          if (WIFEXITED (estatus))
+            g_error ("Subprocess %s exited with code %d\n", args[0], WEXITSTATUS (estatus));
+          else if (WIFSIGNALED (estatus))
+            g_error ("Subprocess %s killed by signal %d\n", args[0], WTERMSIG (estatus));
+          else
+            g_error ("Subprocess %s terminated with status %d\n", args[0], estatus);
+          exit (1);
+        }
+    }
+  else
+    {
+      g_error ("Failed to execute %s: %s\n", args[0], error->message);
+      exit (1);
+    }
+}
+
+static void
+ptr_array_extend (GPtrArray *dest, GPtrArray *to_append)
+{
+  int i;
+
+  for (i = 0; i < to_append->len; i++)
+    g_ptr_array_add (dest, to_append->pdata[i]);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  GPtrArray *config_args;
+  GPtrArray *make_args;
+  GPtrArray *makeinstall_args;
+  GPtrArray *args;
+  char **subprocess_env;
+
+  g_type_init ();
+
+  g_set_prgname (argv[0]);
+
+  args = g_ptr_array_new ();
+
+  subprocess_env = g_get_environ ();
+  ot_g_environ_setenv (subprocess_env, "LANG", "C", TRUE);
+  ot_g_environ_unsetenv (subprocess_env, "LC_ALL");
+
+  split_configure_make_args (argc, argv, &config_args, &make_args, &makeinstall_args);
+
+  if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
+    {
+      const char *autogen;
+      char **autogen_env;
+      
+      autogen = find_first_file (G_FILE_TEST_IS_EXECUTABLE, "./autogen", "./autogen.sh", NULL);
+      if (!autogen)
+        ot_util_fatal_literal ("No executable configure or autogen script found"); 
+
+      autogen_env = g_strdupv (subprocess_env);
+      ot_g_environ_setenv (autogen_env, "NOCONFIGURE", "1", TRUE);
+      
+      g_ptr_array_set_size (args, 0);
+      g_ptr_array_add (args, (char*) autogen);
+      g_ptr_array_add (args, NULL);
+      spawn_sync_or_fatal ((char**)args->pdata, autogen_env, 0);
+    }
+
+  if (!g_file_test ("./configure", G_FILE_TEST_IS_EXECUTABLE))
+    ot_util_fatal_literal ("autogen script failed to generate a configure script");
+
+  g_ptr_array_set_size (args, 0);
+  g_ptr_array_add (args, "./configure");
+  ptr_array_extend (args, config_args);
+  g_ptr_array_add (args, NULL);
+  spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, 0);
+    
+  g_ptr_array_set_size (args, 0);
+  g_ptr_array_add (args, "make");
+  ptr_array_extend (args, make_args);
+  g_ptr_array_add (args, NULL);
+  spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
+  
+  g_ptr_array_set_size (args, 0);
+  g_ptr_array_add (args, "make");
+  g_ptr_array_add (args, "install");
+  ptr_array_extend (args, makeinstall_args);
+  g_ptr_array_add (args, NULL);
+  spawn_sync_or_fatal ((char**)args->pdata, subprocess_env, G_SPAWN_SEARCH_PATH);
+
+  return 0;
+}
diff --git a/src/osbuild/ostree-buildone b/src/osbuild/ostree-buildone
new file mode 100644 (file)
index 0000000..2f2fc6d
--- /dev/null
@@ -0,0 +1,304 @@
+#!/usr/bin/python
+#
+# ostree-buildone:
+# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
+# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
+
+# The build output is automatically logged to $TMPDIR/build-$(PWD).log.
+# For example, invoking metabuild in a directory named "foo" will log
+# to /tmp/build-foo.log
+#
+# You can pass arguments to metabuild; if they start with '--', they're
+# given to configure.  Otherwise, they're passed to make.
+#
+# $ metabuild --enable-libfoo  # passed to configure
+# $ metabuild -j 1             # passed to make
+
+import os,sys,subprocess,tempfile,re
+import select,time,stat,fcntl
+
+subprocess_nice_args = []
+
+# In the future we should test for this better; possibly implement a
+# custom fork handler
+try:
+    chrt_args = ['chrt', '--idle', '0']
+    proc = subprocess.Popen(chrt_args + ['true'])
+    if proc.wait() == 0:
+        subprocess_nice_args.extend(chrt_args)
+except OSError, e:
+    pass
+
+try:
+    ionice_args = ['ionice', '-c', '3', '-t']
+    proc = subprocess.Popen(ionice_args + ['true'])
+    if proc.wait() == 0:
+        subprocess_nice_args.extend(ionice_args)
+except OSError, e:
+    pass
+
+warning_re = re.compile(r'(: ((warning)|(error)|(fatal error)): )|(make(\[[0-9]+\])?: \*\*\*)')
+output_whitelist_re = re.compile(r'^(make(\[[0-9]+\])?: Entering directory)|(ostree-build )')
+
+_bold_sequence = None
+_normal_sequence = None
+if os.isatty(1):
+    _bold_sequence = subprocess.Popen(['tput', 'bold'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
+    _normal_sequence = subprocess.Popen(['tput', 'sgr0'], stdout=subprocess.PIPE, stderr=open('/dev/null', 'w')).communicate()[0]
+def _bold(text):
+    if _bold_sequence is not None:
+        return '%s%s%s' % (_bold_sequence, text, _normal_sequence)
+    else:
+        return text
+
+class Mainloop(object):
+    DEFAULT = None
+    def __init__(self):
+        self._running = True
+        self.poll = select.poll()
+        self._timeouts = []
+        self._pid_watches = {}
+        self._fd_callbacks = {}
+
+    @classmethod
+    def get(cls, context):
+        if context is None:
+            if cls.DEFAULT is None:
+                cls.DEFAULT = cls()
+            return cls.DEFAULT
+        raise NotImplementedError("Unknown context %r" % (context, ))
+
+    def watch_fd(self, fd, callback):
+        self.poll.register(fd)
+        self._fd_callbacks[fd] = callback
+
+    def unwatch_fd(self, fd):
+        self.poll.unregister(fd)
+        del self._fd_callbacks[fd]
+
+    def watch_pid(self, pid, callback):
+        self._pid_watches[pid] = callback
+
+    def timeout_add(self, ms, callback):
+        self._timeouts.append((ms, callback))
+
+    def quit(self):
+        self._running = False
+
+    def run_once(self):
+        min_timeout = None
+        if len(self._pid_watches) > 0:
+            min_timeout = 500
+        for (ms, callback) in self._timeouts:
+            if (min_timeout is None) or (ms < min_timeout):
+                min_timeout = ms
+        origtime = time.time() * 1000
+        fds = self.poll.poll(min_timeout)
+        for fd in fds:
+            self._fd_callbacks[fd]()
+        for pid in self._pid_watches:
+            (opid, status) = os.waitpid(pid, os.WNOHANG)
+            if opid != 0:
+                self._pid_watches[pid](opid, status)
+                del self._pid_watches[pid]
+        newtime = time.time() * 1000
+        diff = int(newtime - origtime)
+        if diff < 0: diff = 0
+        for i,(ms, callback) in enumerate(self._timeouts):
+            remaining_ms = ms - diff
+            if remaining_ms <= 0:
+                callback()
+            else:
+                self._timeouts[i] = (remaining_ms, callback)
+
+    def run(self):
+        while self._running:
+            self.run_once()
+
+class FileMonitor(object):
+    def __init__(self):
+        self._paths = {}
+        self._path_modtimes = {}
+        self._timeout = 1000
+        self._timeout_installed = False
+        self._loop = Mainloop.get(None)
+
+    def _stat(self, path):
+        try:
+            st = os.stat(path)
+            return st[stat.ST_MTIME]
+        except OSError, e:
+            return None
+
+    def add(self, path, callback):
+        if path not in self._paths:
+            self._paths[path] = []
+            self._path_modtimes[path] = self._stat(path)
+        self._paths[path].append(callback)
+        if not self._timeout_installed:
+            self._timeout_installed = True
+            self._loop.timeout_add(self._timeout, self._check_files)
+
+    def _check_files(self):
+        for (path,callbacks) in self._paths.iteritems():
+            mtime = self._stat(path)
+            orig_mtime = self._path_modtimes[path]
+            if (mtime is not None) and (orig_mtime is None or (mtime > orig_mtime)):
+                self._path_modtimes[path] = mtime
+                for cb in callbacks:
+                    cb()
+
+_filemon = FileMonitor()
+
+class OutputFilter(object):
+    def __init__(self, filename, output):
+        self.filename = filename
+        self.output = output
+
+        # inherit globals
+        self._warning_re = warning_re
+        self._nonfilter_re = output_whitelist_re
+
+        self._buf = ''
+        self._warning_count = 0
+        self._filtered_line_count = 0
+        _filemon.add(filename, self._on_changed)
+        self._fd = os.open(filename, os.O_RDONLY)
+        fcntl.fcntl(self._fd, fcntl.F_SETFL, os.O_NONBLOCK)
+
+    def _do_read(self):
+        while True:
+            buf = os.read(self._fd, 4096)
+            if buf == '':
+                break
+            self._buf += buf
+        self._flush()
+
+    def _write_last_log_lines(self):
+        _last_line_limit = 100
+        f = open(logfile_path)
+        lines = []
+        for line in f:
+            if line.startswith('ostree-build '):
+                continue
+            lines.append(line)
+            if len(lines) > _last_line_limit:
+                lines.pop(0)
+        f.close()
+        for line in lines:
+            self.output.write('| ')
+            self.output.write(line)
+
+    def _flush(self):
+        while True:
+            p = self._buf.find('\n')
+            if p < 0:
+                break
+            line = self._buf[0:p]
+            self._buf = self._buf[p+1:]
+            match = self._warning_re.search(line)
+            if match:
+                self._warning_count += 1
+                self.output.write(line + '\n')
+            else:    
+                match = self._nonfilter_re.search(line)
+                if match:
+                    self.output.write(line + '\n')
+                else:
+                    self._filtered_line_count += 1
+
+    def _on_changed(self):
+        self._do_read()
+
+    def start(self):
+        self._do_read()
+
+    def finish(self, successful):
+        self._do_read()
+        if not successful:
+            self._write_last_log_lines()
+            pass
+        self.output.write("ostree-build %s: %d warnings\n" % ('success' if successful else _bold('failed'),
+                                                            self._warning_count, ))
+        self.output.write("ostree-build: full log path: %s\n" % (logfile_path, ))
+        
+        if successful:
+            for f in os.listdir('_build'):
+                path = os.path.join('_build', f)
+                if f.startswith('artifact-'):
+                    self.output.write("ostree-build: created artifact: %s\n" % (f, ))
+        sys.exit(0 if successful else 1)
+
+def _on_makeinstall_exit(pid, estatus):
+    _output_filter.finish(estatus == 0)
+
+def _on_make_exit(pid, estatus):
+    if estatus == 0:
+        args = list(subprocess_nice_args)
+        args.append('ostree-buildone-makeinstall-split-artifacts')
+        _logfile_f.write("Running: %r\n" % (args, ))
+        _logfile_f.flush()
+        proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
+        _loop.watch_pid(proc.pid, _on_makeinstall_exit)
+    else:
+        _output_filter.finish(False)
+
+def _get_version():
+    if not os.path.isdir('.git'):
+        sys.stderr.write("ostree-buildone: error: Couldn't find .git directory")
+        sys.exit(1)
+
+    proc = subprocess.Popen(['git', 'describe'], stdout=subprocess.PIPE)
+    output = proc.communicate()[0].strip()
+    if proc.wait() != 0:
+        proc = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE)
+        if proc.wait() != 0:
+            sys.stderr.write("ostree-buildone: error: git rev-parse HEAD failed")
+            sys.exit(1)
+        output = proc.communicate()[0].strip()
+    return output
+
+if __name__ == '__main__':
+    user_tmpdir = os.environ.get('XDG_RUNTIME_DIR')
+    if user_tmpdir is None:
+        user_tmpdir = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'metabuild-%s' % (os.getuid(), ))
+    else:
+        user_tmpdir = os.path.join(user_tmpdir, 'ostree-build') 
+
+    os.environ['OSBUILD_VERSION'] = _get_version()
+
+    if os.path.isdir('_build'):
+        for filename in os.listdir('_build'):
+            path = os.path.join('_build', filename)
+            if filename.startswith('artifact-'):
+                os.unlink(path)
+
+    if not os.path.isdir(user_tmpdir):
+        os.makedirs(user_tmpdir)
+    logfile_path = os.path.join(user_tmpdir, '%s.log' % (os.path.basename(os.getcwd()), ))
+    try:
+        os.unlink(logfile_path)
+    except OSError, e:
+        pass
+    logfile_write_fd = os.open(logfile_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL)
+    global _logfile_f
+    _logfile_f = os.fdopen(logfile_write_fd, "w")
+    sys.stdout.write('ostree-build: logging to %r\n' % (logfile_path, ))
+    sys.stdout.flush()
+
+    global _output_filter
+    _output_filter = OutputFilter(logfile_path, sys.stdout)
+    _output_filter.start()
+
+    args = list(subprocess_nice_args)
+    args.append('ostree-buildone-make')
+    args.extend(sys.argv[1:])
+    devnull=open('/dev/null')
+    _logfile_f.write("Running: %r\n" % (args, ))
+    _logfile_f.flush()
+    proc = subprocess.Popen(args, stdin=devnull, stdout=logfile_write_fd, stderr=logfile_write_fd)
+
+    global _loop
+    _loop = Mainloop.get(None)
+    _loop.watch_pid(proc.pid, _on_make_exit)
+    _loop.run()
diff --git a/src/osbuild/ostree-buildone-make b/src/osbuild/ostree-buildone-make
new file mode 100644 (file)
index 0000000..b6e89f5
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/python
+
+# ostree-buildone-raw: Generic build system wrapper
+# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
+# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
+
+# ostree-buildone-raw wraps systems that implement the GNOME build API:
+# http://people.gnome.org/~walters/docs/build-api.txt
+
+import os,sys,subprocess,tempfile,re
+from multiprocessing import cpu_count
+import select,time
+
+root = None
+
+prefix = '/usr'
+
+# libdir detection
+if os.path.isdir('/lib64'):
+    libdir=os.path.join(prefix, 'lib64')
+else:
+    libdir=os.path.join(prefix, 'lib')
+
+default_buildapi_jobs = ['-j', '%d' % (cpu_count() * 2, )]
+configargs = ['--prefix=' + prefix,
+              '--libdir=' + libdir,
+              '--sysconfdir=/etc',
+              '--localstatedir=/var',
+              '--bindir=' + os.path.join(prefix, 'bin'),
+              '--sbindir=' + os.path.join(prefix, 'sbin'),
+              '--datadir=' + os.path.join(prefix, 'share'),
+              '--includedir=' + os.path.join(prefix, 'include'),
+              '--libexecdir=' + os.path.join(prefix, 'libexec'),
+              '--mandir=' + os.path.join(prefix, 'share', 'man'),
+              '--infodir=' + os.path.join(prefix, 'share', 'info')]
+makeargs = ['make']
+
+top_srcdir=os.getcwd()
+
+for arg in sys.argv[1:]:
+    if arg.startswith('--'):
+        configargs.append(arg)
+    else:
+        makeargs.append(arg)
+
+def log(msg):
+    fullmsg = 'ostree-buildone: ' + msg + '\n'
+    sys.stdout.write(fullmsg)
+    sys.stdout.flush()
+
+def fatal(msg):
+    log(msg)
+    sys.exit(1)
+
+def run_sync(args, env=None):
+    log("Running: %r" % (args, ))
+    f = open('/dev/null', 'r')
+    proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
+                            close_fds=True, env=env)
+    f.close()
+    returncode = proc.wait()
+    log("pid %d exited with code %d" % (proc.pid, returncode))
+    if returncode != 0:
+        sys.exit(1)
+
+class BuildSystemScanner(object):
+    @classmethod
+    def _find_file(cls, names):
+        for name in names:
+            if os.path.exists(name):
+                return name
+        return None
+
+    @classmethod
+    def get_configure_source_script(cls):
+        return cls._find_file(('./configure.ac', './configure.in'))
+
+    @classmethod
+    def get_configure_script(cls):
+        return cls._find_file(('./configure', ))
+
+    @classmethod
+    def get_bootstrap_script(cls):
+        return cls._find_file(('./autogen.sh', ))
+
+    @classmethod
+    def get_silent_rules(cls):
+        src = cls.get_configure_source_script()
+        if not src:
+            return False
+        f = open(src)
+        for line in f:
+            if line.find('AM_SILENT_RULES') >= 0:
+                f.close()
+                return True
+        f.close()
+        return False
+
+def _search_file(filename, pattern):
+    f = open(filename)
+    for line in f:
+        if line.startswith(pattern):
+            f.close()
+            return line
+    f.close()
+    return None
+
+def _find_buildapi_makevariable(name):
+    var = '.%s:' % (name, )
+    line = None
+    if os.path.exists('Makefile.in'):
+        line = _search_file('Makefile.in', var)
+    if not line and os.path.exists('Makefile'):
+        line = _search_file('Makefile', var)
+    return line is not None
+
+def phase_bootstrap():        
+    have_configure = BuildSystemScanner.get_configure_script() 
+    have_configure_source = BuildSystemScanner.get_configure_source_script()
+    if not (have_configure or have_configure_source):
+        fatal("No configure or bootstrap script detected; unknown buildsystem")
+        return
+
+    need_v1 = BuildSystemScanner.get_silent_rules()
+    if need_v1:
+        log("Detected AM_SILENT_RULES, adding --disable-silent-rules to configure")
+        configargs.append('--disable-silent-rules')
+
+    if have_configure:
+        phase_configure()
+    else:
+        bootstrap = BuildSystemScanner.get_bootstrap_script()
+        if bootstrap:
+            log("Detected bootstrap script: %s, using it" % (bootstrap, ))
+            args = [bootstrap]
+            args.extend(configargs)
+            # Add NOCONFIGURE; GNOME style scripts use this
+            env = dict(os.environ)
+            env['NOCONFIGURE'] = '1'
+            run_sync(args, env=env)
+        else:
+            log("No bootstrap script found; using generic autoreconf")
+            run_sync(['autoreconf', '-f', '-i'])
+        phase_configure()
+
+def phase_configure():
+    use_builddir = True
+    doesnot_support_builddir = _find_buildapi_makevariable('buildapi-no-builddir')
+    if doesnot_support_builddir:
+        log("Found .buildapi-no-builddir; copying source tree to _build")
+        shutil.rmtree('_build')
+        os.mkdir('_build')
+        shutil.copytree('.', '_build', symlinks=True,
+                        ignore=shutil.ignore_patterns('_build'))
+        use_builddir = False
+        builddir = '.'
+    else:
+        builddir = '_build'
+
+    if not use_builddir:
+        configdir = './'
+    else:
+        configdir = os.getcwd()
+        builddir = builddir
+        log("Using build directory %r" % (builddir, ))
+        if not os.path.isdir(builddir):
+            os.mkdir(builddir)
+        os.chdir(builddir)
+
+    configstatus = 'config.status'
+    if not os.path.exists(configstatus):
+        args = [os.path.join(configdir, 'configure')]
+        args.extend(configargs)
+        run_sync(args)
+    else:
+        log("Found %s, skipping configure" % (configstatus, ))
+    phase_build()
+
+build_status = False
+
+def phase_build():
+    if not os.path.exists('Makefile'):
+        log("No Makefile found")
+        sys.exit(1)
+    args = makeargs
+    user_specified_jobs = False
+    for arg in args:
+        if arg == '-j':
+            user_specified_jobs = True
+
+    if not user_specified_jobs:
+        notparallel = _find_buildapi_makevariable('NOTPARALLEL')
+        if not notparallel:
+            log("Didn't find NOTPARALLEL, using parallel make by default")
+            args.extend(default_buildapi_jobs)
+
+    run_sync(args)
+
+def phase_complete():
+    sys.exit(0)
+
+log("invocation arguments: %r" % (sys.argv, ))
+
+# Start off the process
+phase_bootstrap()
diff --git a/src/osbuild/ostree-buildone-makeinstall-split-artifacts b/src/osbuild/ostree-buildone-makeinstall-split-artifacts
new file mode 100644 (file)
index 0000000..85167a5
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/python
+
+# ostree-buildone-raw: Generic build system wrapper
+# Copyright 2010, 2011 Colin Walters <walters@verbum.org>
+# Licensed under the new-BSD license (http://www.opensource.org/licenses/bsd-license.php)
+
+import os,sys,re,subprocess
+import tempfile,shutil
+
+_devel_regexps = map(re.compile,
+                     [r'/usr/include/',
+                      r'/usr/share/pkgconfig/',
+                      r'/.*lib(?:|(?:32)|(?:64))/pkgconfig/.*\.pc',
+                      r'/.*lib(?:|(?:32)|(?:64))/.*\.so$'])
+
+def log(msg):
+    fullmsg = 'ostree-buildone: ' + msg + '\n'
+    sys.stdout.write(fullmsg)
+    sys.stdout.flush()
+
+tempfiles = []
+
+def do_exit(code):
+    for tmpname in tempfiles:
+        if os.path.isdir(tmpname):
+            shutil.rmtree(tmpname)
+        else:
+            try:
+                os.unlink(tmpname)
+                pass
+            except OSError, e:
+                pass
+    sys.exit(code)
+
+def fatal(msg):
+    log(msg)
+    do_exit(1)
+
+def run_sync(args, env=None):
+    log("Running: %r" % (args, ))
+    f = open('/dev/null', 'r')
+    proc = subprocess.Popen(args, stdin=f, stdout=sys.stdout, stderr=sys.stderr,
+                            close_fds=True, env=env)
+    f.close()
+    returncode = proc.wait()
+    if returncode == 0:
+        func = log
+    else:
+        func = fatal
+    func("pid %d exited with code %d" % (proc.pid, returncode))
+
+basename=os.path.basename(os.getcwd())
+artifact_prefix='artifact-%s,%s' % (basename, os.environ['OSBUILD_VERSION'])
+origdir=os.getcwd()
+os.chdir('_build')
+
+if not os.path.exists('Makefile'):
+    log("No Makefile found")
+    do_exit(1)
+
+(fd,fakeroot_temp)=tempfile.mkstemp(prefix='ostree-fakeroot-%s-' % (basename,))
+os.close(fd)
+tempfiles.append(fakeroot_temp)
+tempdir = tempfile.mkdtemp(prefix='ostree-build-%s-' % (basename,))
+tempfiles.append(tempdir)
+args = ['fakeroot', '-s', fakeroot_temp, 'make', 'install', 'DESTDIR=' + tempdir]
+run_sync(args)
+
+devel_files = set()
+runtime_files = set()
+
+oldpwd=os.getcwd()
+os.chdir(tempdir)
+for root, dirs, files in os.walk('.'):
+    for filename in files:
+        path = os.path.join(root, filename)
+        matched = False
+        for r in _devel_regexps:
+            if r.match(path[1:]):
+                devel_files.add(path)
+                matched = True
+                break
+        if not matched:    
+            runtime_files.add(path)
+os.chdir(oldpwd)
+
+def make_artifact(name, from_files):
+    artifact_target = '%s-%s.tar.gz' % (artifact_prefix, name)
+    (fd,filelist_temp)=tempfile.mkstemp(prefix='ostree-filelist-%s-%s' % (basename, name))
+    os.close(fd)
+    tempfiles.append(filelist_temp)
+    f = open(filelist_temp, 'w')
+    for filename in from_files:
+        f.write(filename)
+        f.write('\n')
+    f.close()
+    args = ['fakeroot', '-i', fakeroot_temp, 'tar', '-c', '-z', '-C', tempdir, '-f', artifact_target, '-T', filelist_temp]
+    run_sync(args)
+
+if devel_files:
+    make_artifact('devel', devel_files)
+make_artifact('runtime', runtime_files)
+
+do_exit(0)
diff --git a/src/ostree/main.c b/src/ostree/main.c
new file mode 100644 (file)
index 0000000..cf0e89c
--- /dev/null
@@ -0,0 +1,127 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <string.h>
+
+#include "ot-builtins.h"
+
+static OstreeBuiltin builtins[] = {
+  { "checkout", ostree_builtin_checkout, 0 },
+  { "diff", ostree_builtin_diff, 0 },
+  { "init", ostree_builtin_init, 0 },
+  { "commit", ostree_builtin_commit, 0 },
+  { "compose", ostree_builtin_compose, 0 },
+  { "log", ostree_builtin_log, 0 },
+#ifdef HAVE_LIBSOUP_GNOME
+  { "pull", ostree_builtin_pull, 0 },
+#endif
+  { "fsck", ostree_builtin_fsck, 0 },
+  { "remote", ostree_builtin_remote, 0 },
+  { "rev-parse", ostree_builtin_rev_parse, 0 },
+  { "remote", ostree_builtin_remote, 0 },
+  { "run-triggers", ostree_builtin_run_triggers, 0 },
+  { "show", ostree_builtin_show, 0 },
+  { NULL }
+};
+
+static int
+usage (char **argv, gboolean is_error)
+{
+  OstreeBuiltin *builtin = builtins;
+  void (*print_func) (const gchar *format, ...);
+
+  if (is_error)
+    print_func = g_printerr;
+  else
+    print_func = g_print;
+
+  print_func ("usage: %s --repo=PATH COMMAND [options]\n",
+              argv[0]);
+  print_func ("Builtin commands:\n");
+
+  while (builtin->name)
+    {
+      print_func ("  %s\n", builtin->name);
+      builtin++;
+    }
+  return (is_error ? 1 : 0);
+}
+
+
+int
+main (int    argc,
+      char **argv)
+{
+  OstreeBuiltin *builtin;
+  const char *cmd;
+  const char *repo;
+
+  g_type_init ();
+
+  g_set_prgname (argv[0]);
+
+  builtin = builtins;
+
+  if (argc < 3)
+    return usage (argv, 1);
+  
+  if (!g_str_has_prefix (argv[1], "--repo="))
+    return usage (argv, 1);
+  repo = argv[1] + strlen ("--repo=");
+
+  cmd = argv[2];
+
+  while (builtin->name)
+    {
+      GError *error = NULL;
+      if (strcmp (cmd, builtin->name) == 0)
+        {
+          int i;
+          int tmp_argc;
+          char **tmp_argv;
+
+          tmp_argc = argc - 2;
+          tmp_argv = g_new0 (char *, tmp_argc + 1);
+
+          tmp_argv[0] = (char*)builtin->name;
+          for (i = 0; i < tmp_argc; i++)
+            tmp_argv[i+1] = argv[i+3];
+          if (!builtin->fn (tmp_argc, tmp_argv, repo, &error))
+            {
+              g_free (tmp_argv);
+              g_printerr ("%s\n", error->message);
+              g_clear_error (&error);
+              return 1;
+            }
+          g_free (tmp_argv);
+          return 0;
+        }
+      builtin++;
+    }
+  
+  g_printerr ("Unknown command '%s'\n", cmd);
+  return usage (argv, 1);
+}
diff --git a/src/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c
new file mode 100644 (file)
index 0000000..90493fc
--- /dev/null
@@ -0,0 +1,79 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+  { NULL }
+};
+
+gboolean
+ostree_builtin_checkout (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *commit;
+  const char *destination;
+  GFile *destf = NULL;
+
+  context = g_option_context_new ("COMMIT DESTINATION - Check out a commit into a filesystem tree");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 3)
+    {
+      gchar *help = g_option_context_get_help (context, TRUE, NULL);
+      g_printerr ("%s\n", help);
+      g_free (help);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "COMMIT and DESTINATION must be specified");
+      goto out;
+    }
+  
+  commit = argv[1];
+  destination = argv[2];
+  
+  if (!ostree_repo_checkout (repo, commit, destination, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  g_clear_object (&destf);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c
new file mode 100644 (file)
index 0000000..3d4d6b9
--- /dev/null
@@ -0,0 +1,335 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <gio/gunixoutputstream.h>
+
+#include <glib/gi18n.h>
+
+static gboolean separator_null;
+static int from_fd = -1;
+static gboolean from_stdin;
+static char *from_file;
+static char *metadata_text_path;
+static char *metadata_bin_path;
+static char *subject;
+static char *body;
+static char *parent;
+static char *branch;
+
+static GOptionEntry options[] = {
+  { "subject", 's', 0, G_OPTION_ARG_STRING, &subject, "One line subject", "subject" },
+  { "body", 'm', 0, G_OPTION_ARG_STRING, &body, "Full description", "body" },
+  { "metadata-variant-text", 0, 0, G_OPTION_ARG_FILENAME, &metadata_text_path, "File containing g_variant_print() output", "path" },
+  { "metadata-variant", 0, 0, G_OPTION_ARG_FILENAME, &metadata_bin_path, "File containing serialized variant, in host endianness", "path" },
+  { "branch", 'b', 0, G_OPTION_ARG_STRING, &branch, "Branch", "branch" },
+  { "parent", 'p', 0, G_OPTION_ARG_STRING, &parent, "Parent commit", "commit" },
+  { "from-fd", 0, 0, G_OPTION_ARG_INT, &from_fd, "Read new tree files from fd", "file descriptor" },
+  { "from-stdin", 0, 0, G_OPTION_ARG_NONE, &from_stdin, "Read new tree files from stdin", "file descriptor" },
+  { "from-file", 0, 0, G_OPTION_ARG_FILENAME, &from_file, "Read new tree files from another file", "path" },
+  { "separator-null", 0, 0, G_OPTION_ARG_NONE, &separator_null, "", "Use '\\0' as filename separator, as with find -print0" },
+  { NULL }
+};
+
+typedef struct {
+  GFile *dir;
+  char separator;
+  GOutputStream *out;
+  GCancellable *cancellable;
+} FindThreadData;
+
+static gboolean
+find (const char *basepath,
+      GFile *dir,
+      char separator,
+      GOutputStream *out,
+      GCancellable *cancellable,
+      GError  **error);
+
+static gboolean
+find_write_child (const char *basepath,
+                  GFile      *dir,
+                  char        separator,
+                  GOutputStream *out,
+                  GFileInfo  *finfo,
+                  GCancellable *cancellable,
+                  GError    **error)
+{
+  gboolean ret = FALSE;
+  guint32 type;
+  const char *name;
+  char buf[1];
+  char *child_path = NULL;
+  GString *child_trimmed_path = NULL;
+  GFile *child = NULL;
+  gsize bytes_written;
+
+  type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
+  name = g_file_info_get_attribute_byte_string (finfo, "standard::name");
+
+  child = g_file_get_child (dir, name);
+
+  if (type == G_FILE_TYPE_DIRECTORY)
+    {
+      if (!find (basepath, child, separator, out, cancellable, error))
+        goto out;
+    }
+
+  child_path = g_file_get_path (child);
+  child_trimmed_path = g_string_new (child_path + strlen (basepath));
+  if (!*(child_trimmed_path->str))
+    {
+      /* do nothing - we implicitly add the root . */
+    }
+  else
+    {
+      g_assert (*(child_trimmed_path->str) == '/');
+      g_string_insert_c (child_trimmed_path, 0, '.');
+
+      if (!g_output_stream_write_all (out, child_trimmed_path->str, child_trimmed_path->len,
+                                      &bytes_written, cancellable, error))
+        goto out;
+      buf[0] = separator;
+      if (!g_output_stream_write_all (out, buf, 1, &bytes_written,
+                                      cancellable, error))
+        goto out;
+    }
+      
+  ret = TRUE;
+ out:
+  g_string_free (child_trimmed_path, TRUE);
+  child_trimmed_path = NULL;
+  g_free (child_path);
+  child_path = NULL;
+  g_clear_object (&child);
+  return ret;
+}
+
+static gboolean
+find (const char *basepath,
+      GFile *dir,
+      char separator,
+      GOutputStream *out,
+      GCancellable *cancellable,
+      GError  **error)
+{
+  gboolean ret = FALSE;
+  GError *temp_error = NULL;
+  GFileEnumerator *enumerator = NULL;
+  GFileInfo *finfo = NULL;
+
+  enumerator = g_file_enumerate_children (dir, "standard::type,standard::name", 
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          cancellable, error);
+  if (!enumerator)
+    goto out;
+
+  while ((finfo = g_file_enumerator_next_file (enumerator, cancellable, error)) != NULL)
+    {
+      if (!find_write_child (basepath, dir, separator, out, finfo, cancellable, error))
+        goto out;
+      g_clear_object (&finfo);
+    }
+  if (temp_error)
+    {
+      g_propagate_error (error, temp_error);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  g_clear_object (&finfo);
+  g_clear_object (&enumerator);
+  return ret;
+}
+
+static gpointer
+find_thread (gpointer data)
+{
+  FindThreadData *tdata = data;
+  GError *error = NULL;
+  char *path;
+  
+  path = g_file_get_path (tdata->dir);
+  if (!find (path, tdata->dir, tdata->separator, tdata->out,
+             tdata->cancellable, &error))
+    {
+      g_printerr ("%s", error->message);
+      g_clear_error (&error);
+    }
+  g_free (path);
+  g_clear_object (&(tdata->dir));
+  g_clear_object (&(tdata->out));
+  return NULL;
+}
+
+gboolean
+ostree_builtin_commit (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  char *dir = NULL;
+  GChecksum *commit_checksum = NULL;
+  char separator;
+  GVariant *metadata = NULL;
+  GMappedFile *metadata_mappedf = NULL;
+  GFile *metadata_f = NULL;
+  gboolean temp_fd = -1;
+  int pipefd[2] = { -1, -1 };
+  GOutputStream *out = NULL;
+  FindThreadData fdata;
+
+  context = g_option_context_new ("[DIR] - Commit a new revision");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (argc > 1)
+    dir = g_strdup (argv[1]);
+  else
+    dir = g_get_current_dir ();
+
+  if (g_str_has_suffix (dir, "/"))
+    dir[strlen (dir) - 1] = '\0';
+
+  separator = separator_null ? '\0' : '\n';
+
+  if (!*dir)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Invalid empty directory");
+      goto out;
+    }
+
+  if (metadata_text_path || metadata_bin_path)
+    {
+      metadata_mappedf = g_mapped_file_new (metadata_text_path ? metadata_text_path : metadata_bin_path, FALSE, error);
+      if (!metadata_mappedf)
+        goto out;
+      if (metadata_text_path)
+        {
+          metadata = g_variant_parse (G_VARIANT_TYPE ("a{sv}"),
+                                      g_mapped_file_get_contents (metadata_mappedf),
+                                      g_mapped_file_get_contents (metadata_mappedf) + g_mapped_file_get_length (metadata_mappedf),
+                                      NULL, error);
+          if (!metadata)
+            goto out;
+        }
+      else if (metadata_bin_path)
+        {
+          metadata_f = ot_util_new_file_for_path (metadata_bin_path);
+          if (!ot_util_variant_map (metadata_f, G_VARIANT_TYPE ("a{sv}"), &metadata, error))
+            goto out;
+        }
+      else
+        g_assert_not_reached ();
+    }
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (!branch)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "A branch must be specified with --branch");
+      goto out;
+    }
+
+  if (!subject)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "A subject must be specified with --subject");
+      goto out;
+    }
+
+  if (!(from_file || from_fd >= 0 || from_stdin))
+    {
+      /* We're using the current directory */
+
+    }
+
+  if (from_stdin)
+    from_fd = 0;
+  else if (from_file)
+    {
+      temp_fd = ot_util_open_file_read (from_file, error);
+      if (temp_fd < 0)
+        {
+          g_prefix_error (error, "Failed to open '%s': ", from_file);
+          goto out;
+        }
+      from_fd = temp_fd;
+    }
+  else
+    {
+      if (pipe (pipefd) < 0)
+        {
+          ot_util_set_error_from_errno (error, errno);
+          goto out;
+        }
+
+      out = (GOutputStream*)g_unix_output_stream_new (pipefd[1], TRUE);
+      from_fd = pipefd[0];
+
+      fdata.dir = ot_util_new_file_for_path (dir);
+      fdata.separator = separator;
+      fdata.out = out;
+      fdata.cancellable = NULL;
+
+      if (g_thread_create_full (find_thread, &fdata, 0, FALSE, FALSE, G_THREAD_PRIORITY_NORMAL, error) == NULL)
+        goto out;
+
+      out = NULL;
+    }
+
+  if (!ostree_repo_commit_from_filelist_fd (repo, branch, parent, subject, body, metadata,
+                                            dir, from_fd, separator,
+                                            &commit_checksum, error))
+    goto out;
+
+  ret = TRUE;
+  g_print ("%s\n", g_checksum_get_string (commit_checksum));
+ out:
+  g_clear_object (&out);
+  if (temp_fd >= 0)
+    (void)close (temp_fd);
+  if (pipefd[0] > 0)
+    (void) close (pipefd[0]);
+  if (pipefd[1] > 0)
+    (void) close (pipefd[1]);
+  g_free (dir);
+  if (metadata_mappedf)
+    g_mapped_file_unref (metadata_mappedf);
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  if (commit_checksum)
+    g_checksum_free (commit_checksum);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-compose.c b/src/ostree/ot-builtin-compose.c
new file mode 100644 (file)
index 0000000..31cfacc
--- /dev/null
@@ -0,0 +1,307 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static char *compose_metadata_path;
+
+static GOptionEntry options[] = {
+  { "out-metadata", 0, 0, G_OPTION_ARG_FILENAME, &compose_metadata_path, "Output a file containing serialized metadata about the compose, in host endianness", "path" },
+  { NULL }
+};
+
+static void
+rm_rf (GFile *path)
+{
+  GFileInfo *finfo = NULL;
+  GFileEnumerator *path_enum = NULL;
+  guint32 type;
+  
+  finfo = g_file_query_info (path, OSTREE_GIO_FAST_QUERYINFO,
+                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                             NULL, NULL);
+  if (!finfo)
+    goto out;
+
+  type = g_file_info_get_attribute_uint32 (finfo, "standard::type");
+  if (type == G_FILE_TYPE_DIRECTORY)
+    {
+      path_enum = g_file_enumerate_children (path, OSTREE_GIO_FAST_QUERYINFO, 
+                                             G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                             NULL, NULL);
+      if (!path_enum)
+        goto out;
+
+      
+      g_clear_object (&finfo);
+      while ((finfo = g_file_enumerator_next_file (path_enum, NULL, NULL)) != NULL)
+        {
+          GFile *child = g_file_get_child (path, g_file_info_get_attribute_byte_string (finfo, "standard::name"));
+          rm_rf (child);
+          g_clear_object (&child);
+          g_clear_object (&finfo);
+        }
+    }
+
+  (void) g_file_delete (path, NULL, NULL);
+
+ out:
+  g_clear_object (&finfo);
+  g_clear_object (&path_enum);
+}
+
+static gboolean
+merge_dir (GFile    *destination,
+           GFile    *src,
+           GError  **error)
+{
+  gboolean ret = FALSE;
+  char *dest_path = NULL;
+  char *src_path = NULL;
+  GError *temp_error = NULL;
+  GFileInfo *src_fileinfo = NULL;
+  GFileInfo *dest_fileinfo = NULL;
+  GFileEnumerator *src_enum = NULL;
+  GFile *dest_subfile = NULL;
+  GFile *src_subfile = NULL;
+  const char *name;
+  guint32 type;
+
+  dest_path = g_file_get_path (destination);
+  src_path = g_file_get_path (src);
+
+  dest_fileinfo = g_file_query_info (destination, OSTREE_GIO_FAST_QUERYINFO,
+                                     G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                     NULL, &temp_error);
+  if (dest_fileinfo)
+    {
+      type = g_file_info_get_attribute_uint32 (dest_fileinfo, "standard::type");
+      if (type != G_FILE_TYPE_DIRECTORY)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Attempting to replace non-directory %s with directory %s",
+                       dest_path, src_path);
+          goto out;
+        }
+
+      src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO, 
+                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                            NULL, error);
+      if (!src_enum)
+        goto out;
+
+      while ((src_fileinfo = g_file_enumerator_next_file (src_enum, NULL, &temp_error)) != NULL)
+        {
+          type = g_file_info_get_attribute_uint32 (src_fileinfo, "standard::type");
+          name = g_file_info_get_attribute_byte_string (src_fileinfo, "standard::name");
+      
+          dest_subfile = g_file_get_child (destination, name);
+          src_subfile = g_file_get_child (src, name);
+
+          if (type == G_FILE_TYPE_DIRECTORY)
+            {
+              if (!merge_dir (dest_subfile, src_subfile, error))
+                goto out;
+            }
+          else
+            {
+              if (!g_file_delete (dest_subfile, NULL, &temp_error))
+                {
+                  if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+                    g_clear_error (&temp_error);
+                  else
+                    {
+                      g_propagate_error (error, temp_error);
+                      goto out;
+                    }
+                }
+              if (!g_file_move (src_subfile, dest_subfile, 0, NULL, NULL, NULL, error))
+                goto out;
+            }
+          
+          g_clear_object (&dest_subfile);
+          g_clear_object (&src_subfile);
+        }
+      if (temp_error)
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+    }
+  else if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+    {
+      g_clear_error (&temp_error);
+      if (!g_file_move (src, destination, 0, NULL, NULL, NULL, error))
+        goto out;
+    }
+  else
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (dest_path);
+  g_free (src_path);
+  g_clear_object (&src_fileinfo);
+  g_clear_object (&dest_fileinfo);
+  g_clear_object (&src_enum);
+  g_clear_object (&dest_subfile);
+  g_clear_object (&src_subfile);
+  return ret;
+}
+
+static gboolean
+compose_branch_on_dir (OstreeRepo *repo,
+                       GFile *destination,
+                       const char *branch,
+                       GVariantBuilder *metadata_builder,
+                       GError **error)
+{
+  char *destpath = NULL;
+  char *branchpath = NULL;
+  GFile *branchf = NULL;
+  GFileEnumerator *enumerator = NULL;
+  gboolean ret = FALSE;
+  char *branchrev = NULL;
+
+  if (!ostree_repo_resolve_rev (repo, branch, &branchrev, error))
+    goto out;
+  
+  destpath = g_file_get_path (destination);
+  if (g_str_has_suffix (destpath, "/"))
+    destpath[strlen (destpath) - 1] = '\0';
+  branchpath = g_strconcat (destpath, "-tmp-checkout-", branchrev, NULL);
+  branchf = ot_util_new_file_for_path (branchpath);
+
+  g_print ("Checking out %s (commit %s)...\n", branch, branchrev);
+  if (!ostree_repo_checkout (repo, branchrev, branchpath, NULL, error))
+    goto out;
+  g_print ("...done\n");
+  g_print ("Merging over destination...\n");
+  if (!merge_dir (destination, branchf, error))
+    goto out;
+
+  if (metadata_builder)
+    g_variant_builder_add (metadata_builder, "(ss)", branch, branchrev);
+
+  ret = TRUE;
+ out:
+  if (branchf)
+    rm_rf (branchf);
+  g_clear_object (&enumerator);
+  g_clear_object (&branchf);
+  g_free (branchrev);
+  g_free (destpath);
+  g_free (branchpath);
+  return ret;
+}
+
+gboolean
+ostree_builtin_compose (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *destination;
+  GFile *destf = NULL;
+  gboolean compose_metadata_builder_initialized = FALSE;
+  GVariantBuilder compose_metadata_builder;
+  gboolean commit_metadata_builder_initialized = FALSE;
+  GVariantBuilder commit_metadata_builder;
+  GVariant *commit_metadata = NULL;
+  GFile *metadata_f = NULL;
+  int i;
+
+  context = g_option_context_new ("DESTINATION BRANCH1 BRANCH2 ... - Merge multiple commits into a single filesystem tree");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 3)
+    {
+      gchar *help = g_option_context_get_help (context, TRUE, NULL);
+      g_printerr ("%s\n", help);
+      g_free (help);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               "DESTINATION and at least one COMMIT must be specified");
+      goto out;
+    }
+
+  destination = argv[1];
+  destf = ot_util_new_file_for_path (destination);
+  
+  if (compose_metadata_path)
+    {
+      compose_metadata_builder_initialized = TRUE;
+      g_variant_builder_init (&compose_metadata_builder, G_VARIANT_TYPE ("a(ss)"));
+    }
+  
+  for (i = 2; i < argc; i++)
+    {
+      const char *branch = argv[i];
+      
+      if (!compose_branch_on_dir (repo, destf, branch, compose_metadata_builder_initialized ? &compose_metadata_builder : NULL, error))
+        goto out;
+    }
+
+  if (compose_metadata_path)
+    {
+      commit_metadata_builder_initialized = TRUE;
+      g_variant_builder_init (&commit_metadata_builder, G_VARIANT_TYPE ("a{sv}"));
+
+      g_variant_builder_add (&commit_metadata_builder, "{sv}",
+                             "ostree-compose", g_variant_builder_end (&compose_metadata_builder));
+      compose_metadata_builder_initialized = FALSE;
+
+      metadata_f = ot_util_new_file_for_path (compose_metadata_path);
+
+      commit_metadata = g_variant_builder_end (&commit_metadata_builder);
+      if (!ot_util_variant_save (metadata_f, commit_metadata, NULL, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (compose_metadata_builder_initialized)
+    g_variant_builder_clear (&compose_metadata_builder);
+  if (commit_metadata_builder_initialized)
+    g_variant_builder_clear (&commit_metadata_builder);
+  if (context)
+    g_option_context_free (context);
+  if (commit_metadata)
+    g_variant_unref (commit_metadata);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  g_clear_object (&destf);
+  g_clear_object (&metadata_f);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-diff.c b/src/ostree/ot-builtin-diff.c
new file mode 100644 (file)
index 0000000..15344bf
--- /dev/null
@@ -0,0 +1,85 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+  { NULL }
+};
+
+gboolean
+ostree_builtin_diff (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  const char *target;
+  const char *rev;
+  GFile *targetf = NULL;
+  GPtrArray *modified = NULL;
+  GPtrArray *removed = NULL;
+  GPtrArray *added = NULL;
+
+  context = g_option_context_new ("REV TARGETDIR - Compare directory TARGETDIR against revision REV");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 3)
+    {
+      gchar *help = g_option_context_get_help (context, TRUE, NULL);
+      g_printerr ("%s\n", help);
+      g_free (help);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               "REV and TARGETDIR must be specified");
+      goto out;
+    }
+
+  rev = argv[1];
+  target = argv[2];
+  targetf = ot_util_new_file_for_path (target);
+  
+  if (!ostree_repo_diff (repo, rev, targetf, &modified, &removed, &added, NULL, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_clear_object (&repo);
+  g_clear_object (&targetf);
+  if (modified)
+    g_ptr_array_free (modified, TRUE);
+  if (removed)
+    g_ptr_array_free (removed, TRUE);
+  if (added)
+    g_ptr_array_free (added, TRUE);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c
new file mode 100644 (file)
index 0000000..3cc72b9
--- /dev/null
@@ -0,0 +1,233 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+  { NULL }
+};
+
+typedef struct {
+  guint n_objects;
+  gboolean had_error;
+} OtFsckData;
+
+static gboolean
+checksum_packed_file (OtFsckData   *data,
+                      const char   *path,
+                      GChecksum   **out_checksum,
+                      GError      **error)
+{
+  gboolean ret = FALSE;
+  GChecksum *ret_checksum = NULL;
+  GFile *file = NULL;
+  char *metadata_buf = NULL;
+  GVariant *metadata = NULL;
+  GVariant *xattrs = NULL;
+  GFileInputStream *in = NULL;
+  guint32 metadata_len;
+  guint32 version, uid, gid, mode;
+  guint64 content_len;
+  gsize bytes_read;
+  char buf[8192];
+
+  file = ot_util_new_file_for_path (path);
+
+  in = g_file_read (file, NULL, error);
+  if (!in)
+    goto out;
+      
+  if (!g_input_stream_read_all ((GInputStream*)in, &metadata_len, 4, &bytes_read, NULL, error))
+    goto out;
+      
+  metadata_len = GUINT32_FROM_BE (metadata_len);
+      
+  metadata_buf = g_malloc (metadata_len);
+      
+  if (!g_input_stream_read_all ((GInputStream*)in, metadata_buf, metadata_len, &bytes_read, NULL, error))
+    goto out;
+
+  metadata = g_variant_new_from_data (G_VARIANT_TYPE (OSTREE_PACK_FILE_VARIANT_FORMAT),
+                                      metadata_buf, metadata_len, FALSE, NULL, NULL);
+      
+  g_variant_get (metadata, "(uuuu@a(ayay)t)",
+                 &version, &uid, &gid, &mode,
+                 &xattrs, &content_len);
+  uid = GUINT32_FROM_BE (uid);
+  gid = GUINT32_FROM_BE (gid);
+  mode = GUINT32_FROM_BE (mode);
+  content_len = GUINT64_FROM_BE (content_len);
+
+  ret_checksum = g_checksum_new (G_CHECKSUM_SHA256);
+
+  do
+    {
+      if (!g_input_stream_read_all ((GInputStream*)in, buf, sizeof(buf), &bytes_read, NULL, error))
+        goto out;
+      g_checksum_update (ret_checksum, (guint8*)buf, bytes_read);
+    }
+  while (bytes_read > 0);
+
+  ostree_checksum_update_stat (ret_checksum, uid, gid, mode);
+  g_checksum_update (ret_checksum, (guint8*)g_variant_get_data (xattrs), g_variant_get_size (xattrs));
+
+  ret = TRUE;
+  *out_checksum = ret_checksum;
+  ret_checksum = NULL;
+ out:
+  if (ret_checksum)
+    g_checksum_free (ret_checksum);
+  g_free (metadata_buf);
+  g_clear_object (&file);
+  g_clear_object (&in);
+  if (metadata)
+   g_variant_unref (metadata);
+  if (xattrs)
+    g_variant_unref (xattrs);
+  return ret;
+}
+
+static void
+object_iter_callback (OstreeRepo  *repo,
+                      const char    *path,
+                      GFileInfo     *file_info,
+                      gpointer       user_data)
+{
+  OtFsckData *data = user_data;
+  struct stat stbuf;
+  GChecksum *checksum = NULL;
+  GError *error = NULL;
+  char *dirname = NULL;
+  char *checksum_prefix = NULL;
+  char *checksum_string = NULL;
+  char *filename_checksum = NULL;
+  gboolean packed = FALSE;
+  OstreeObjectType objtype;
+  char *dot;
+
+  /* nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
+     if (nlinks < 2 && !quiet)
+     g_printerr ("note: floating object: %s\n", path); */
+
+  if (g_str_has_suffix (path, ".meta"))
+    objtype = OSTREE_OBJECT_TYPE_META;
+  else if (g_str_has_suffix (path, ".file"))
+    objtype = OSTREE_OBJECT_TYPE_FILE;
+  else if (g_str_has_suffix (path, ".packfile"))
+    {
+      objtype = OSTREE_OBJECT_TYPE_FILE;
+     packed = TRUE;
+    }
+  else
+    g_assert_not_reached ();
+
+  if (packed && objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      if (!checksum_packed_file (data, path, &checksum, &error))
+        goto out;
+    }
+  else
+    {
+      if (!ostree_stat_and_checksum_file (-1, path, objtype, &checksum, &stbuf, &error))
+        goto out;
+    }
+
+  filename_checksum = g_strdup (g_file_info_get_name (file_info));
+  dot = strrchr (filename_checksum, '.');
+  g_assert (dot != NULL);
+  *dot = '\0';
+  
+  dirname = g_path_get_dirname (path);
+  checksum_prefix = g_path_get_basename (dirname);
+  checksum_string = g_strconcat (checksum_prefix, filename_checksum, NULL);
+  
+  if (strcmp (checksum_string, g_checksum_get_string (checksum)) != 0)
+    {
+      data->had_error = TRUE;
+      g_printerr ("ERROR: corrupted object '%s' expected checksum: %s\n",
+                  path, g_checksum_get_string (checksum));
+    }
+
+  data->n_objects++;
+
+ out:
+  if (checksum != NULL)
+    g_checksum_free (checksum);
+  g_free (dirname);
+  g_free (checksum_prefix);
+  g_free (checksum_string);
+  g_free (filename_checksum);
+  if (error != NULL)
+    {
+      g_printerr ("%s\n", error->message);
+      g_clear_error (&error);
+    }
+}
+
+gboolean
+ostree_builtin_fsck (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  OtFsckData data;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+
+  context = g_option_context_new ("- Check the repository for consistency");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  data.n_objects = 0;
+  data.had_error = FALSE;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (!ostree_repo_iter_objects (repo, object_iter_callback, &data, error))
+    goto out;
+
+  if (data.had_error)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Encountered filesystem consistency errors");
+      goto out;
+    }
+  if (!quiet)
+    g_printerr ("Total Objects: %u\n", data.n_objects);
+
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-init.c b/src/ostree/ot-builtin-init.c
new file mode 100644 (file)
index 0000000..c58d639
--- /dev/null
@@ -0,0 +1,112 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean archive;
+
+static GOptionEntry options[] = {
+  { "archive", 0, 0, G_OPTION_ARG_NONE, &archive, "Initialize repository as archive", NULL },
+  { NULL }
+};
+
+#define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
+                                 "repo_version=0\n")
+
+
+gboolean
+ostree_builtin_init (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context = NULL;
+  gboolean ret = FALSE;
+  GFile *repodir = NULL;
+  GFile *child = NULL;
+  GFile *grandchild = NULL;
+  GString *config_data = NULL;
+
+  context = g_option_context_new ("- Initialize a new empty repository");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repodir = ot_util_new_file_for_path (repo_path);
+
+  child = g_file_get_child (repodir, "config");
+
+  config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
+  g_string_append_printf (config_data, "archive=%s\n", archive ? "true" : "false");
+  if (!g_file_replace_contents (child,
+                                config_data->str,
+                                config_data->len,
+                                NULL, FALSE, 0, NULL,
+                                NULL, error))
+    goto out;
+  g_clear_object (&child);
+
+  child = g_file_get_child (repodir, "objects");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+  g_clear_object (&child);
+
+  child = g_file_get_child (repodir, "tmp");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+  g_clear_object (&child);
+
+  child = g_file_get_child (repodir, "refs");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+
+  grandchild = g_file_get_child (child, "heads");
+  if (!g_file_make_directory (grandchild, NULL, error))
+    goto out;
+  g_clear_object (&grandchild);
+
+  grandchild = g_file_get_child (child, "remotes");
+  if (!g_file_make_directory (grandchild, NULL, error))
+    goto out;
+  g_clear_object (&grandchild);
+
+  g_clear_object (&child);
+
+  child = g_file_get_child (repodir, "tags");
+  if (!g_file_make_directory (child, NULL, error))
+    goto out;
+  g_clear_object (&child);
+
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  if (config_data)
+    g_string_free (config_data, TRUE);
+  g_clear_object (&repodir);
+  g_clear_object (&child);
+  g_clear_object (&grandchild);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-log.c b/src/ostree/ot-builtin-log.c
new file mode 100644 (file)
index 0000000..ff82fde
--- /dev/null
@@ -0,0 +1,154 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+  { NULL }
+};
+
+gboolean
+ostree_builtin_log (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  GOutputStream *pager = NULL;
+  const char *rev;
+  GVariant *commit = NULL;
+  char *resolved_rev = NULL;
+
+  context = g_option_context_new ("- Show revision log");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (argc < 2)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "A revision must be specified");
+      goto out;
+    }
+                   
+  rev = argv[1];
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (!ot_util_spawn_pager (&pager, error))
+    goto out;
+
+  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+    goto out;
+
+  while (TRUE)
+    {
+      OstreeSerializedVariantType type;
+      char *formatted = NULL;
+      guint32 version;
+      const char *parent;
+      const char *subject;
+      const char *body;
+      guint64 timestamp;
+      const char *contents;
+      const char *root_metadata;
+      GDateTime *time_obj = NULL;
+      char *formatted_date = NULL;
+      const char *body_newline;
+      gsize bytes_written;
+      GVariant *commit_metadata = NULL;
+      char *formatted_metadata = NULL;
+      
+      if (commit)
+        g_variant_unref (commit);
+      if (!ostree_repo_load_variant (repo, resolved_rev, &type, &commit, error))
+        goto out;
+
+      /* Ignore commit metadata for now */
+      g_variant_get (commit, "(u@a{sv}&s&s&st&s&s)",
+                     &version, &commit_metadata, &parent, &subject, &body,
+                     &timestamp, &contents, &root_metadata);
+      version = GUINT32_FROM_BE (version);
+      timestamp = GUINT64_FROM_BE (timestamp);
+      time_obj = g_date_time_new_from_unix_utc (timestamp);
+      formatted_date = g_date_time_format (time_obj, "%a %b %d %H:%M:%S %Y %z");
+      g_date_time_unref (time_obj);
+      time_obj = NULL;
+
+      formatted_metadata = g_variant_print (commit_metadata, TRUE);
+      g_variant_unref (commit_metadata);
+      formatted = g_strdup_printf ("commit %s\nSubject: %s\nDate: %s\nMetadata: %s\n\n",
+                                   resolved_rev, subject, formatted_date, formatted_metadata);
+      g_free (formatted_metadata);
+      g_free (formatted_date);
+      formatted_date = NULL;
+      
+      if (!g_output_stream_write_all (pager, formatted, strlen (formatted), &bytes_written, NULL, error))
+        {
+          g_free (formatted);
+          goto out;
+        }
+      g_free (formatted);
+      
+      body_newline = strchr (body, '\n');
+      do {
+        gsize len;
+        if (!g_output_stream_write_all (pager, "    ", 4, &bytes_written, NULL, error))
+          goto out;
+        len = body_newline ? body_newline - body : strlen (body);
+        if (!g_output_stream_write_all (pager, body, len, &bytes_written, NULL, error))
+          goto out;
+        if (!g_output_stream_write_all (pager, "\n\n", 2, &bytes_written, NULL, error))
+          goto out;
+        body_newline = strchr (body, '\n');
+        if (!body_newline)
+          break;
+        else
+          body_newline += 1;
+      } while (*body_newline);
+
+      if (strcmp (parent, "") == 0)
+        break;
+      g_free (resolved_rev);
+      resolved_rev = g_strdup (parent);
+    }
+
+  if (!g_output_stream_close (pager, NULL, error))
+    goto out;
+  ret = TRUE;
+ out:
+  g_free (resolved_rev);
+  if (context)
+    g_option_context_free (context);
+  if (commit)
+    g_variant_unref (commit);
+  g_clear_object (&repo);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-pull.c b/src/ostree/ot-builtin-pull.c
new file mode 100644 (file)
index 0000000..d702219
--- /dev/null
@@ -0,0 +1,351 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+#include <libsoup/soup-gnome.h>
+
+static GOptionEntry options[] = {
+  { NULL }
+};
+
+static gboolean
+fetch_uri (OstreeRepo  *repo,
+           SoupSession *soup,
+           SoupURI     *uri,
+           char       **temp_filename,
+           GError     **error)
+{
+  gboolean ret = FALSE;
+  SoupMessage *msg = NULL;
+  guint response;
+  char *template = NULL;
+  int fd;
+  SoupBuffer *buf = NULL;
+  GFile *tempf = NULL;
+  
+  msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+  
+  response = soup_session_send_message (soup, msg);
+  if (response != 200)
+    {
+      char *uri_string = soup_uri_to_string (uri, FALSE);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to retrieve '%s': %d %s",
+                   uri_string, response, msg->reason_phrase);
+      g_free (uri_string);
+      goto out;
+    }
+
+  template = g_strdup_printf ("%s/tmp-fetchXXXXXX", ostree_repo_get_path (repo));
+  
+  fd = g_mkstemp (template);
+  if (fd < 0)
+    {
+      ot_util_set_error_from_errno (error, errno);
+      goto out;
+    }
+  close (fd);
+  tempf = ot_util_new_file_for_path (template);
+
+  buf = soup_message_body_flatten (msg->response_body);
+
+  if (!g_file_replace_contents (tempf, buf->data, buf->length, NULL, FALSE, 0, NULL, NULL, error))
+    goto out;
+  
+  *temp_filename = template;
+  template = NULL;
+
+  ret = TRUE;
+ out:
+  g_free (template);
+  g_clear_object (&msg);
+  g_clear_object (&tempf);
+  return ret;
+}
+
+static gboolean
+store_object (OstreeRepo  *repo,
+              SoupSession *soup,
+              SoupURI     *baseuri,
+              const char  *object,
+              OstreeObjectType objtype,
+              gboolean    *did_exist,
+              GError     **error)
+{
+  gboolean ret = FALSE;
+  char *filename = NULL;
+  char *objpath = NULL;
+  char *relpath = NULL;
+  SoupURI *obj_uri = NULL;
+
+  objpath = ostree_get_relative_object_path (object, objtype, TRUE);
+  obj_uri = soup_uri_copy (baseuri);
+  relpath = g_build_filename (soup_uri_get_path (obj_uri), objpath, NULL);
+  soup_uri_set_path (obj_uri, relpath);
+
+  if (!fetch_uri (repo, soup, obj_uri, &filename, error))
+    goto out;
+
+  if (!ostree_repo_store_packfile (repo, object, filename, objtype, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  if (obj_uri)
+    soup_uri_free (obj_uri);
+  if (filename)
+    (void) unlink (filename);
+  g_free (filename);
+  g_free (objpath);
+  g_free (relpath);
+  return ret;
+}
+
+static gboolean
+store_tree_recurse (OstreeRepo   *repo,
+                    SoupSession  *soup,
+                    SoupURI      *base_uri,
+                    const char   *rev,
+                    GError      **error)
+{
+  gboolean ret = FALSE;
+  GVariant *tree = NULL;
+  GVariant *files_variant = NULL;
+  GVariant *dirs_variant = NULL;
+  OstreeSerializedVariantType metatype;
+  gboolean did_exist;
+  int i, n;
+
+  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+    goto out;
+
+  if (!did_exist)
+    {
+      if (!ostree_repo_load_variant (repo, rev, &metatype, &tree, error))
+        goto out;
+      
+      if (metatype != OSTREE_SERIALIZED_TREE_VARIANT)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Tree metadata '%s' has wrong type %d, expected %d",
+                       rev, metatype, OSTREE_SERIALIZED_TREE_VARIANT);
+          goto out;
+        }
+      
+      /* PARSE OSTREE_SERIALIZED_TREE_VARIANT */
+      g_variant_get_child (tree, 2, "@a(ss)", &files_variant);
+      g_variant_get_child (tree, 3, "@a(sss)", &dirs_variant);
+      
+      n = g_variant_n_children (files_variant);
+      for (i = 0; i < n; i++)
+        {
+          const char *filename;
+          const char *checksum;
+
+          g_variant_get_child (files_variant, i, "(ss)", &filename, &checksum);
+
+          if (!store_object (repo, soup, base_uri, checksum, OSTREE_OBJECT_TYPE_FILE, &did_exist, error))
+            goto out;
+        }
+      
+      n = g_variant_n_children (dirs_variant);
+      for (i = 0; i < n; i++)
+        {
+          const char *dirname;
+          const char *tree_checksum;
+          const char *meta_checksum;
+
+          g_variant_get_child (dirs_variant, i, "(sss)",
+                               &dirname, &tree_checksum, &meta_checksum);
+
+          if (!store_tree_recurse (repo, soup, base_uri, tree_checksum, error))
+            goto out;
+
+          if (!store_object (repo, soup, base_uri, meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+            goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  if (tree)
+    g_variant_unref (tree);
+  if (files_variant)
+    g_variant_unref (files_variant);
+  if (dirs_variant)
+    g_variant_unref (dirs_variant);
+  return ret;
+}
+
+static gboolean
+store_commit_recurse (OstreeRepo   *repo,
+                      SoupSession  *soup,
+                      SoupURI      *base_uri,
+                      const char   *rev,
+                      GError      **error)
+{
+  gboolean ret = FALSE;
+  GVariant *commit = NULL;
+  OstreeSerializedVariantType metatype;
+  const char *tree_contents_checksum;
+  const char *tree_meta_checksum;
+  gboolean did_exist;
+
+  if (!store_object (repo, soup, base_uri, rev, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+    goto out;
+
+  if (!did_exist)
+    {
+      if (!ostree_repo_load_variant (repo, rev, &metatype, &commit, error))
+        goto out;
+      
+      if (metatype != OSTREE_SERIALIZED_COMMIT_VARIANT)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Commit '%s' has wrong type %d, expected %d",
+                       rev, metatype, OSTREE_SERIALIZED_COMMIT_VARIANT);
+          goto out;
+        }
+      
+      /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+      g_variant_get_child (commit, 6, "&s", &tree_contents_checksum);
+      g_variant_get_child (commit, 7, "&s", &tree_meta_checksum);
+      
+      if (!store_object (repo, soup, base_uri, tree_meta_checksum, OSTREE_OBJECT_TYPE_META, &did_exist, error))
+        goto out;
+      
+      if (!store_tree_recurse (repo, soup, base_uri, tree_contents_checksum, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (commit)
+    g_variant_unref (commit);
+  return ret;
+}
+                      
+gboolean
+ostree_builtin_pull (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  const char *remote;
+  const char *branch;
+  char *remote_branch_ref_path = NULL;
+  char *key = NULL;
+  char *baseurl = NULL;
+  char *refpath = NULL;
+  char *temppath = NULL;
+  GKeyFile *config = NULL;
+  SoupURI *base_uri = NULL;
+  SoupURI *target_uri = NULL;
+  SoupSession *soup = NULL;
+  char *rev = NULL;
+
+  context = g_option_context_new ("REMOTE BRANCH - Download data from remote repository");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 3)
+    {
+      ot_util_usage_error (context, "REMOTE and BRANCH must be specified", error);
+      goto out;
+    }
+
+  remote = argv[1];
+  branch = argv[2];
+
+  config = ostree_repo_get_config (repo);
+
+  key = g_strdup_printf ("remote \"%s\"", remote);
+  baseurl = g_key_file_get_string (config, key, "url", error);
+  if (!baseurl)
+    goto out;
+  base_uri = soup_uri_new (baseurl);
+  if (!base_uri)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to parse url '%s'", baseurl);
+      goto out;
+    }
+  target_uri = soup_uri_copy (base_uri);
+  g_free (refpath);
+  refpath = g_build_filename (soup_uri_get_path (target_uri), "refs", "heads", branch, NULL);
+  soup_uri_set_path (target_uri, refpath);
+  
+  soup = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
+                                             SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_COOKIE_JAR,
+                                             NULL);
+  if (!fetch_uri (repo, soup, target_uri, &temppath, error))
+    goto out;
+
+  rev = ot_util_get_file_contents_utf8 (temppath, error);
+  if (!rev)
+    goto out;
+  g_strchomp (rev);
+
+  if (!ostree_validate_checksum_string (rev, error))
+    goto out;
+
+  if (!store_commit_recurse (repo, soup, base_uri, rev, error))
+    goto out;
+
+  if (!ostree_repo_write_ref (repo, FALSE, branch, rev, error))
+    goto out;
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  if (temppath)
+    (void) unlink (temppath);
+  g_free (temppath);
+  g_free (key);
+  g_free (rev);
+  g_free (baseurl);
+  g_free (refpath);
+  g_free (remote_branch_ref_path);
+  g_clear_object (&soup);
+  if (base_uri)
+    soup_uri_free (base_uri);
+  if (target_uri)
+    soup_uri_free (target_uri);
+  g_clear_object (&repo);
+  g_clear_object (&soup);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-remote.c b/src/ostree/ot-builtin-remote.c
new file mode 100644 (file)
index 0000000..f9d4a2c
--- /dev/null
@@ -0,0 +1,104 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+  { NULL }
+};
+
+static void
+usage_error (GOptionContext *context, const char *message, GError **error)
+{
+  gchar *help = g_option_context_get_help (context, TRUE, NULL);
+  g_printerr ("%s\n", help);
+  g_free (help);
+  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       message);
+}
+
+gboolean
+ostree_builtin_remote (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *op;
+  GKeyFile *config = NULL;
+
+  context = g_option_context_new ("OPERATION [args] - Control remote repository configuration");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 2)
+    {
+      usage_error (context, "OPERATION must be specified", error);
+      goto out;
+    }
+
+  op = argv[1];
+
+  config = ostree_repo_copy_config (repo);
+
+  if (!strcmp (op, "add"))
+    {
+      char *key;
+      if (argc < 4)
+        {
+          usage_error (context, "NAME and URL must be specified", error);
+          goto out;
+        }
+      key = g_strdup_printf ("remote \"%s\"", argv[2]);
+      g_key_file_set_string (config, key, "url", argv[3]);
+      g_free (key);
+    }
+  else
+    {
+      usage_error (context, "Unknown operation", error);
+      goto out;
+    }
+
+  if (!ostree_repo_write_config (repo, config, error))
+    goto out;
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  if (config)
+    g_key_file_free (config);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-rev-parse.c b/src/ostree/ot-builtin-rev-parse.c
new file mode 100644 (file)
index 0000000..3a83cd7
--- /dev/null
@@ -0,0 +1,77 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static GOptionEntry options[] = {
+  { NULL }
+};
+
+gboolean
+ostree_builtin_rev_parse (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  const char *rev = "master";
+  char *resolved_rev = NULL;
+  GVariant *variant = NULL;
+  char *formatted_variant = NULL;
+
+  context = g_option_context_new ("REV - Output the target of a rev");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 2)
+    {
+      ot_util_usage_error (context, "REV must be specified", error);
+      goto out;
+    }
+  rev = argv[1];
+
+  if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+    goto out;
+
+  g_print ("%s\n", resolved_rev);
+  ret = TRUE;
+ out:
+  g_free (resolved_rev);
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  if (variant)
+    g_variant_unref (variant);
+  g_free (formatted_variant);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-run-triggers.c b/src/ostree/ot-builtin-run-triggers.c
new file mode 100644 (file)
index 0000000..dd33211
--- /dev/null
@@ -0,0 +1,79 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean quiet;
+
+static GOptionEntry options[] = {
+  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Don't display informational messages", NULL },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_run_triggers (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  OstreeCheckout *checkout = NULL;
+  const char *dir;
+
+  context = g_option_context_new ("DIR - Run trigger scripts for directory");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc < 1)
+    {
+      gchar *help = g_option_context_get_help (context, TRUE, NULL);
+      g_printerr ("%s\n", help);
+      g_free (help);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "DIR must be specified");
+      goto out;
+    }
+
+  dir = argv[1];
+
+  checkout = ostree_checkout_new (repo, dir);
+  if (!ostree_checkout_run_triggers (checkout, error))
+    goto out;
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  g_clear_object (&checkout);
+  return ret;
+}
diff --git a/src/ostree/ot-builtin-show.c b/src/ostree/ot-builtin-show.c
new file mode 100644 (file)
index 0000000..3a2e9c3
--- /dev/null
@@ -0,0 +1,197 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ot-builtins.h"
+#include "ostree.h"
+
+#include <glib/gi18n.h>
+
+static gboolean print_compose;
+static char* print_variant_type;
+
+static GOptionEntry options[] = {
+  { "print-compose", 0, 0, G_OPTION_ARG_NONE, &print_compose, "If given, show the branches which make up the given compose commit", NULL },
+  { "print-variant-type", 0, 0, G_OPTION_ARG_STRING, &print_variant_type, "If given, argument should be a filename and it will be interpreted as this type", NULL },
+  { NULL }
+};
+
+static void
+print_variant (GVariant *variant)
+{
+  char *formatted_variant = NULL;
+
+  formatted_variant = g_variant_print (variant, TRUE);
+  g_print ("%s\n", formatted_variant);
+
+  g_free (formatted_variant);
+}
+
+static gboolean
+do_print_variant_generic (const GVariantType *type,
+                          const char *filename,
+                          GError **error)
+{
+  gboolean ret = FALSE;
+  GFile *f = NULL;
+  GVariant *variant = NULL;
+
+  f = ot_util_new_file_for_path (filename);
+
+  if (!ot_util_variant_map (f, type, &variant, error))
+    goto out;
+
+  print_variant (variant);
+
+  ret = TRUE;
+ out:
+  if (variant)
+    g_variant_unref (variant);
+  g_clear_object (&f);
+  return ret;
+}
+
+static gboolean
+show_repo_meta (OstreeRepo  *repo,
+                const char *rev,
+                const char *resolved_rev,
+                GError **error)
+{
+  OstreeSerializedVariantType type;
+  gboolean ret = FALSE;
+  GVariant *variant = NULL;
+
+  if (!ostree_repo_load_variant (repo, resolved_rev, &type, &variant, error))
+    goto out;
+  g_print ("Object: %s\nType: %d\n", resolved_rev, type);
+  print_variant (variant);
+
+  ret = TRUE;
+ out:
+  if (variant)
+    g_variant_unref (variant);
+  return ret;
+}
+
+static gboolean
+do_print_compose (OstreeRepo  *repo,
+                  const char *rev,
+                  const char *resolved_rev,
+                  GError **error)
+{
+  gboolean ret = FALSE;
+  GVariant *variant = NULL;
+  GVariant *metadata = NULL;
+  GVariant *compose_contents = NULL;
+  GVariantIter *viter = NULL;
+  GHashTable *metadata_hash = NULL;
+  const char *branch;
+  const char *branchrev;
+
+  if (!ostree_repo_load_variant_checked (repo, OSTREE_SERIALIZED_COMMIT_VARIANT,
+                                         resolved_rev, &variant, error))
+    goto out;
+      
+  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+  metadata = g_variant_get_child_value (variant, 1);
+  metadata_hash = ot_util_variant_asv_to_hash_table (metadata);
+  
+  compose_contents = g_hash_table_lookup (metadata_hash, "ostree-compose");
+  if (!compose_contents)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Commit %s does not have compose metadata key \"ostree-compose\"", resolved_rev);
+      goto out;
+    }
+
+  g_variant_get_child (compose_contents, 0, "a(ss)", &viter);
+  while (g_variant_iter_next (viter, "(&s&s)", &branch, &branchrev))
+    {
+      g_print ("%s %s\n", branch, branchrev);
+    }
+
+  ret = TRUE;
+ out:
+  if (variant)
+    g_variant_unref (variant);
+  if (viter)
+    g_variant_iter_free (viter);
+  if (metadata)
+    g_variant_unref (metadata);
+  if (metadata_hash)
+    g_hash_table_destroy (metadata_hash);
+  return ret;
+}
+
+gboolean
+ostree_builtin_show (int argc, char **argv, const char *repo_path, GError **error)
+{
+  GOptionContext *context;
+  gboolean ret = FALSE;
+  OstreeRepo *repo = NULL;
+  const char *rev = "master";
+  char *resolved_rev = NULL;
+
+  context = g_option_context_new ("- Output a metadata object");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  repo = ostree_repo_new (repo_path);
+  if (!ostree_repo_check (repo, error))
+    goto out;
+
+  if (argc > 1)
+    rev = argv[1];
+
+  if (print_compose)
+    {
+      if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+        goto out;
+
+      if (!do_print_compose (repo, rev, resolved_rev, error))
+        goto out;
+    }
+  else if (print_variant_type)
+    {
+      if (!do_print_variant_generic (G_VARIANT_TYPE (print_variant_type), rev, error))
+        goto out;
+    }
+  else
+    {
+      if (!ostree_repo_resolve_rev (repo, rev, &resolved_rev, error))
+        goto out;
+
+      if (!show_repo_meta (repo, rev, resolved_rev, error))
+        goto out;
+    }
+  ret = TRUE;
+ out:
+  g_free (resolved_rev);
+  if (context)
+    g_option_context_free (context);
+  g_clear_object (&repo);
+  return ret;
+}
diff --git a/src/ostree/ot-builtins.h b/src/ostree/ot-builtins.h
new file mode 100644 (file)
index 0000000..8337cc8
--- /dev/null
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2011 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#ifndef __OSTREE_BUILTINS__
+#define __OSTREE_BUILTINS__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  OSTREE_BUILTIN_FLAG_NONE = 0,
+} OstreeBuiltinFlags;
+
+typedef struct {
+  const char *name;
+  gboolean (*fn) (int argc, char **argv, const char *repo, GError **error);
+  int flags; /* OstreeBuiltinFlags */
+} OstreeBuiltin;
+
+gboolean ostree_builtin_checkout (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_commit (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_compose (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_diff (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_init (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_log (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_pull (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_run_triggers (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_fsck (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_show (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_rev_parse (int argc, char **argv, const char *repo, GError **error);
+gboolean ostree_builtin_remote (int argc, char **argv, const char *repo, GError **error);
+
+G_END_DECLS
+
+#endif